Resources & Tech · Hospice Solution

Pure date arithmetic, gated money, and a chart that cannot be rewritten.

A hospice agency is judged on dates. When the election happened, when the notice was filed, when the certification was signed, when the group last reviewed the patient, when the assessment window closed. This module puts every one of those calculations in pure functions with their own unit tests, so the worklist, the nightly scan and the claim builder answer the same question the same way — and puts every rate and threshold in configuration, so no dollar figure lives in the code.

Overview

The engineering record behind the episode spine, the benefit-period engine, the compliance classifiers and the per-diem claim gates.

Technology stack by layer
LayerWhat runs thereWhy
Agency front endNext.js 14 App Router, React 18, TypeScript and Tailwind, as its own application on its own domainHospice is episode-based comfort care with a different chart, different worklists and a different regulatory spine from ambulatory care. A separate application beats a mode flag on an EMR that would then have to mean two things everywhere.
State and data fetchingTanStack Query over a shared API client, with the platform's cookie session rather than a bearer tokenThe session is an httpOnly cookie issued by the same platform that serves the portal and the EMR, so the hospice app inherits authentication rather than implementing a second one.
APIFastAPI, async throughout, one router per hospice domain — episodes, referrals, clinical, compliance, billing, QAPI, chart, staff and the programme layerRouters stay thin and domain-shaped. The rules that decide compliance and money live in the service layer beneath them, where they can be tested without a database or an HTTP client.
Rules layerPure functions with no session — date arithmetic, state machines, classifiers, per-diem selection and rate mathThe certification worklist, the nightly scan, the episode API and the claim builder all call the same functions, so three surfaces cannot form three opinions about one episode.
DatabaseMySQL 8 with an entity column on every hospice table and no ORM relationship from an episode to the shared patient rowThe patients table is shared platform ground. Episodes reference it by identifier and stitch the few labels a page needs in one query, which keeps hospice from taking ownership of a table three other products write to.
MigrationsAlembic, a linear chain — the entity-type change first, then episodes, referrals, clinical, billing, the programme layer, QAPI, and staff with room and boardThe entity-type migration has to run before anything else, because the enum on the entities table is the single source of truth for what a hospice organisation is.
Background jobsThe platform's leader-elected scheduler, with a nightly hospice compliance scanLeader election means one instance runs the scan in a multi-container deployment. Deduplication stops the same finding notifying every night, and a heartbeat lets the health check tell a failed run from a run that never happened.
ConfigurationOne root environment file — per-diem rates, the cap amount, QAPI targets, vital thresholds, and the four vendor credential pairsA rate change is a deployment setting, not a release. It also means the codebase carries no dollar amounts, which is the honest position for software that does not decide what a payer pays.
Outside servicesGateway modules for the clearinghouse, visit verification, fax and the survey vendor, each reading credentials only from configurationAll four follow the platform's optional-integration contract: with no credentials the work is held with a message naming the settings, rather than failing or silently disappearing.
TestsThirteen rule-test modules over the pure functions — benefit periods, episodes, referrals, clinical, compliance, billing, cap, QAPI, volunteers, bereavement, staff and the gatewaysEverything that decides a deadline, a severity or a dollar amount is testable without a database, which is why those tests exist at all rather than being deferred behind fixtures.
Architecture

How the engine actually works

Each section describes a mechanism that exists in the codebase today, not a pattern we admire.

Benefit periods

One implementation of the calendar

The benefit-period module is pure date arithmetic over a single input, the election date. It returns the length of any period — ninety days for the first two, sixty for every one after — the inclusive start and end bounds of a period, the period number covering an arbitrary calendar day, the day the recertification window opens fifteen days ahead of the period it certifies, and whether a Face-to-Face encounter is required, which it is from the third period onwards.

Episode lifecycle

The two gates that decide money

Two rules in the episode module decide whether an agency gets paid. The first is the Notice of Election deadline: five calendar days from election, after which every day until the notice is accepted is provider-liable. The second is the recertification guard, which refuses to advance a benefit period unless the certification carries the attending physician's signature and the medical director's signature, and — from the third period — unless a Face-to-Face encounter has been completed.

Compliance

Classifiers, not a rules table

Survey readiness is computed by four classifiers — one each for the Notice of Election, the certification window, the interdisciplinary review cycle and the HOPE windows. Each takes the plain facts of one episode and returns either an alert or nothing, and each is a pure function, so every severity decision is unit-tested rather than argued about. The worklist endpoint and the nightly job both assemble their output from the same four.

The chart

Signed means signed

A visit note is editable until it is signed and immutable afterwards. The guard is a single function that raises when a signature timestamp is present, and its message names the moment of signature and points the caller at an addendum. There is no endpoint that edits a signed note's clinical fields and none that deletes one — a hospice chart is evidence, and the difference between an amended note and a replaced note is precisely what a surveyor is looking for.

Revenue cycle

Rules in code, amounts in configuration

The billing module owns the rules and none of the numbers. It takes a frozen rate structure as an input — routine home care high and low, continuous care hourly, respite daily, general inpatient daily, and the day of care at which routine care steps down — and from that selects the per-diem for a day, splits a routine stay across the step-down boundary, and totals a month. The docstring is explicit that every dollar figure is injected and never hardcoded.

Quality

None is not zero

The QAPI engine computes five indicators from chart facts: pain control from vital readings, visits in the last days of life against death discharge dates, service intensity as visits per patient-week on service, HOPE timeliness against the computed windows, and the live-discharge rate over terminal episode statuses. Each returns a rate in the zero-to-one range, and each returns nothing at all when the denominator is empty.

Programme layer

Obligations that start themselves

Bereavement support runs up to thirteen months after a death, and the module holds the default touchpoint plan every new case starts with. Its month arithmetic clamps to the target month's last day, so a death on the thirty-first of August produces a one-month call on the thirtieth of September rather than rolling into October — a small correctness detail that is invisible until an auditor lines the dates up. Coordinators add contacts freely on top of the default plan, and a case opens automatically when an episode discharges to death.

Gateways

Held, with the setting named

Four outside services are reached through gateway modules that share one result shape — a status, a reference and a note. With credentials present the gateway posts and returns the vendor's reference. With credentials absent it returns a held status and a note naming the exact settings an operator must fill in, and the note says where the work is meanwhile: the claim is stored, the verified visit's timestamps and location persist on the visit row, the fax is queued.

Submission files

A format that admits its own version

HOPE batches are built from completed assessment records, carry a manifest, and are marked submitted once they have gone. The serializer that renders a batch is pure and deterministic — the same inputs produce the same bytes — and holds no database access at all; the export endpoint owns entity scoping and the certification-number guard.

Data model

The records underneath, and the rules that hold them true

Core entities and their invariants
EntityWhat it holdsRule that always holds
hospice_referralsOne inbound referral with its source, clinical summary and contact details, worked through an intake queue.Status moves only along the enumerated transitions; converted and declined are terminal, and conversion writes the patient and the episode in the same transaction.
hospice_episodesOne election of the hospice benefit — election date, level of care, residence, status and discharge disposition.Carries the agency entity from the token; terminal statuses close the stay and a discharge to death opens a bereavement case.
hospice_benefit_periodsThe certified periods of an episode, with certification signatures and the Face-to-Face record.Bounds are derived from the election date, never stored independently; a period will not open until its certification requirements are satisfied.
hospice_visitsDiscipline visit notes — nursing, aide, social work, chaplain and volunteer — documented against the episode.Editable until signed and immutable afterwards; the only route to a correction is an addendum, and no endpoint deletes one.
hospice_idg_meetings and hospice_idg_reviewsThe interdisciplinary meeting with its attendance and one review row per patient discussed.The fifteen-day cycle is computed from each episode's last review; finalising a meeting produces the minutes and stamps the approved plan version.
hospice_poc_versionsVersions of the plan of care, each with the meeting that approved it.Versions are monotonic — a new version supersedes, never overwrites, so the plan in force on any past date is recoverable.
hospice_hope_records and hospice_hope_batchesHOPE assessments against their computed windows, and the submission batches assembled from them.Windows are derived from the admission date; a batch export requires the agency's certification number and carries an explicit format version.
hospice_claimsNotices of election and termination and monthly per-diem claims, with room-and-board claims where configured.No monthly claim before the notice is accepted, none before the prior month is remitted, and none at all if a required rate is unconfigured.
hospice_scheduled_visitsPlanned visits on the calendar, linked to the note they produce.Scheduled moves to completed, missed or cancelled; only a missed visit may return to scheduled, and completed and cancelled are terminal.
hospice_vital_readingsVitals and pain scores captured on a visit or a phone check-in, classified against configured limits.Limits are injected from configuration; absent fields are simply not evaluated, so a pain-only check-in is a valid reading rather than a partial one.
hospice_bereavement_cases and hospice_bereavement_contactsThe thirteen-month bereavement programme for a family, with its scheduled touchpoints and every logged contact.A case opens automatically on a death discharge; touchpoint dates clamp to the target month's last day rather than spilling into the next month.
hospice_volunteers and hospice_volunteer_hoursThe volunteer roster and the hours behind the five-percent Condition of Participation gauge.The share is volunteer minutes over total care minutes — the conservative denominator — and negative inputs raise rather than being silently clamped.
hospice_staff_credentialsLicences, certifications and registrations per staff member, with expiry dates.Classified as current, expiring or expired against a fixed warning window; a null expiry means the credential does not expire and reads as current.
hospice_qapi_pipsPerformance improvement projects recorded as plan-do-study-act cycles alongside the computed indicators.Indicators are computed from chart facts on read; a project records intent and progress and never overwrites a measured rate.
hospice_faxesInbound faxes received through the provider webhook and outbound sends queued from the fax centre.Inbound arrives whether or not outbound is configured; an outbound send with no provider account is queued with the setting named.
Interfaces

What it exchanges, and in which direction

Optional integrations degrade gracefully: with no key configured the product still runs, it just does less.

Platform patient record

Bidirectional

Episodes reference the shared patient row by identifier with no ORM relationship, and the chart's medication, family-contact and document tabs read the platform's own tables. A hospice patient already known to a clinic in the network keeps one medication list.

Entity graph and authentication

Inbound

The agency is a hospice organisation in the platform's entity enum, added by a migration that alters the column before any code reads the new value. Every hospice table carries the entity from the signed-in user's token, never from the request body.

Platform notifications and scheduler

Outbound

The nightly compliance scan runs on the leader-elected scheduler and raises its findings as ordinary platform notifications, deduplicated, with a heartbeat written on success and on failure.

Clearinghouse

Outbound

Notices of election and monthly institutional claims post through a gateway whose credentials come only from configuration. Without them the claim is held with the settings named and resubmits from the same button.

Visit-verification aggregator

Outbound

Check-in and check-out timestamps and location persist on the visit row regardless, and submit to the state aggregator once its credentials are configured; a held record syncs on a later attempt.

Fax provider

Bidirectional

Inbound faxes arrive through the webhook route and land in the inbox as soon as the provider points at it. Outbound sends queue with the setting named until the account is configured.

CMS iQIES

Outbound

HOPE batches are serialized deterministically, gated on the agency's certification number, and stamped with an explicit format version so a pre-validation file can never be mistaken for a certified one.

Survey vendor

Bidirectional

CAHPS measures come from a certified survey vendor and are shown as awaiting configuration until that account exists, rather than being approximated from anything the agency holds.

Security

How access is decided and recorded

Entity scoping
The agency entity is read from the signed-in user's token on every read and every write. A record belonging to another agency is not found rather than forbidden, so a caller learns nothing from the error.
Migration-first enum change
Adding the hospice organisation type required altering the entity-type column before any code could store the value, followed by the derived copies in the models, the request schemas and the admin front end — with a test that fails the suite if those copies ever drift apart.
Shared session, no second auth
The application uses the platform's httpOnly cookie session and signing keys. There is no hospice-specific token format and no second credential store to compromise.
Immutable clinical record
Signed visit notes reject edits with a message pointing to an addendum, and no route deletes one. Plan-of-care versions supersede rather than overwrite.
Credentials only from configuration
All four vendor gateways read their key and endpoint from the deployment's settings. No credential appears in code, in a request body or in a database row, and the settings page reports only whether a pair is present.
Submission guard
The HOPE export refuses to produce a batch without the agency's CMS certification number, so a file can never leave carrying a blank or borrowed identifier.
Money gates in the service layer
The notice-acceptance gate, the sequential-billing rule and the zero-rate refusal are enforced beneath the router, so no future endpoint can reach the claim builder around them.
Shared patient rows stay shared
Chart tabs that read platform medications, contacts and documents go through the platform's own access path rather than a hospice query, so patient-level access rules apply unchanged.

Infrastructure is designed for HIPAA obligations under a BAA. We document the controls and hand over the runbooks; we do not claim a certification that does not exist for software.

Reliability

What keeps it correct under load

Pure rules, no session
Benefit-period arithmetic, the notice deadline, the recertification guard, the compliance classifiers, per-diem selection, the cap position and the volunteer share are all pure functions. Three surfaces calling one function cannot produce three answers.
Atomic conversion
Accepting a referral and admitting the patient is one transaction that writes the patient and the episode together, so a half-converted referral is not a state the system can be left in.
Long-lead item reported first
The recertification guard raises the missing Face-to-Face before missing signatures, because the encounter needs scheduling and a signature needs a phone call. The order of the checks is an operational decision, written down as such.
Month arithmetic that clamps
Bereavement touchpoints add months by clamping to the target month's last day, so a death at the end of a long month does not push its one-month contact into the following month.
Empty denominator returns nothing
Every QAPI indicator returns no value rather than zero when there is nothing to measure, so a quiet quarter cannot render as a quality failure.
Zero-rate refusal
A claim that would price at zero because a rate is unconfigured is refused. A zero-dollar submission looks like a claim and behaves like a loss, which is the worst combination available.
Held rather than dropped
Every vendor gateway returns a held status with the settings named when credentials are absent, and the underlying record — claim, verified visit, queued fax — persists for resubmission.
Leader election, dedup and heartbeat
The nightly compliance scan runs once across a multi-container deployment, suppresses repeat notifications for a standing finding, and writes a heartbeat on success and failure so a silent page is distinguishable from a dead job.
Deterministic serialization
The HOPE batch serializer is pure and deterministic — same inputs, same bytes — with entity scoping and the certification-number guard owned by the endpoint above it.
Named constants over inlined numbers
The period lengths, the recertification window, the period from which a Face-to-Face is required, the notice deadline, the review cycle, the assessment windows, the credential warning window and the volunteer threshold are each a named constant in one place.
Development record

The documents this was built from

Analysis, design discussion, implementation notes and QA written while the work happened. These are engineering artifacts in the product repository, not published pages — listed here so you can see what exists and ask for any of it.

Development-time documentation for Hospice Solution
DocumentKindDateWhat it covers
Hospice separate-service plandocs/hospice/HOSPICE-SERVICE-PLAN.mdPlan2026-09-02The decision to build hospice as a separate product rather than an EMR extension, the regulatory floor, a capability-by-capability disposition against the existing platform, the architecture, and the seven-phase roadmap the build followed.
Service plan, diagrammeddocs/hospice/hospice-service-plan.htmlDesign2026-09-02The visual companion to the plan — flow and architecture diagrams, the gap matrix as charts, and the phased roadmap laid out for review with the client.
Standalone plan documentdocs/hospice/hospice-service-plan-standalone.htmlDesign2026-09-02A self-contained version of the same plan with its diagrams baked in, so it renders identically without a diagram renderer or a network connection.
Forty-two step implementation planplan/html/implementation-plan.htmlPlan2026-09-03Every build step across six groups — platform and data model, clinical charting, compliance and quality, revenue cycle, field operations, and the care network — each carrying a status chip and the file it landed in.
Implementation status recordplan/plan.mdReport2026-09-03The running record of what shipped and when, phase by phase, including which migrations were applied and which end-to-end paths were verified after each deployment.
Client-confirmation prototypeplan/html/Design2026-09-01Twenty-one fully furnished screens with demo authentication, built before the application so the agency could confirm the workflow — buttons, filters, modals and all — rather than approve a wireframe.
Prototype generatorplan/_generate.pyReference2026-09-01The single source the prototype pages are generated from, so the twenty-one screens stay consistent with each other and with the design system rather than drifting page by page.
Rule test suitedevelopment/backend/tests/test_hospice_*.pyQA2026-09-04Thirteen modules of unit tests over the pure rules — benefit periods, episode gates, referral transitions, clinical locks, compliance severities, billing math, the cap, QAPI indicators, volunteers, bereavement, staff credentials and the vendor gateways.
Migration chaindevelopment/backend/alembic/versions/Migration2026-09-04Eight linked migrations, beginning with the entity-type change that had to precede everything else and continuing through episodes, referrals, clinical, billing, the programme layer, quality and staff.
Development seeddevelopment/backend/scripts/seed_hospice_dev.pyRunbook2026-09-01The script that stands up a demonstration agency with its users and a populated census, so every screen can be exercised against real database rows rather than fixtures in the front end.
Web server configurationdeployment/hospice-nginx.confRunbook2026-09-01The reverse-proxy configuration for the hospice hostname, kept in the repository so the deployed environment's routing is reviewable alongside the code it serves.
Vendor credential runbookplan/plan.mdRunbook2026-09-03The four credential pairs an operator adds to the root configuration to enable the clearinghouse, visit verification, fax and survey feeds, and what resubmits automatically once they are in place.
Glossary

Terms used on this page

Episode
One election of the hospice benefit, from admission to discharge, revocation, transfer or death — the record every hospice worklist reads.
Benefit period
A certified stretch of the benefit: two of ninety days, then sixty-day periods without limit, contiguous and inclusive at both ends.
Recertification window
The fifteen days before a period starts, during which its certification may be completed.
Face-to-Face
The encounter required before the third benefit period and every one after; the recertification guard reports it first because it is the long-lead item.
Certification of Terminal Illness
The physician certification behind a benefit period, requiring both the attending physician's and the medical director's signatures.
Notice of Election
The notice filed with the Medicare Administrative Contractor after an election, with a five-calendar-day deadline computed from the election date.
Provider-liable
The status of days between a missed notice deadline and acceptance — the reason the classifier reports the date rather than a generic overdue label.
IDG
The interdisciplinary group whose review of every active patient is due at least every fifteen days.
Plan-of-care version
A monotonic version of the care plan, stamped with the meeting that approved it, so the plan in force on a past date is recoverable.
Addendum
The only permitted change to a signed visit note; the original stays exactly as signed.
HOPE window
One of the computed assessment windows — admission within five days, then day six to fifteen and day sixteen to thirty.
Batch manifest
The record accompanying a set of assessment records submitted together, with the batch marked submitted once it has gone.
Per-diem
The daily rate for a level of care; routine home care splits between a high and a low rate at a configured day of care.
Sequential billing
Medicare's rule that a month's claim waits for the previous month's remittance — enforced before submission rather than discovered on rejection.
Aggregate cap
The ceiling on payments in a cap year, computed as the beneficiary count times a configured amount, tracked continuously.
Held
The gateway status returned when a vendor credential pair is absent: the work persists and the note names the setting to configure.
Volunteer share
Volunteer minutes over total care minutes — the conservative denominator, chosen because it is the reading surveyors use.
Worst-state roll-up
The single status shown for a staff member, taken from their least healthy credential, so one lapse is visible from the roster.
Leader election
The mechanism ensuring exactly one container runs a scheduled job in a multi-instance deployment.
Heartbeat
The record a scheduled job writes on success and on failure, so a quiet result can be told apart from a job that never ran.
Questions

Asked by the people who evaluate this

Why is hospice a separate application instead of a mode in the EMR?

Because the shapes do not overlap. The EMR's primary unit is an encounter; hospice's is an episode with benefit periods and an interdisciplinary cycle. The quality programme is different, the billing is different, and the regulatory spine is different. Folding it in would have made every shared screen mean two things and every shared rule carry a branch. The platform underneath — authentication, the entity graph, the patient record, documents, notifications, the scheduler — is shared, which is where sharing actually pays.

Why keep the date arithmetic in pure functions with no database access?

Because four different surfaces need the same answers: the certification worklist, the nightly scan, the episode API and the claim builder. A module that held a session would have invited each of them to compute periods its own way, and the first divergence would show up as a period boundary that the worklist and the claim disagreed about. One implementation, unit-tested, is the cheapest guarantee available.

Why does the recertification guard check the Face-to-Face first?

Operational sequencing. A missing signature is resolved with a phone call; a missing Face-to-Face needs a scheduled encounter with a nurse practitioner or physician and can take a week. Reporting the quick failure first would let an agency clear it, believe they were done, and discover the slow one afterwards. The order of the checks is a deliberate decision recorded in the code.

Why are there no rates anywhere in the codebase?

Because the software does not decide what a payer pays, and a rate baked into a release becomes wrong on the day CMS updates it. The billing module owns the rules — level-of-care selection, the routine-care step-down, month totals — and takes every amount as an input from the deployment's configuration. It also means this page can describe how billing works without quoting a figure that would be stale by the time you read it.

What actually happens if we have no clearinghouse account yet?

Everything except submission runs. Referrals, census, certifications, the interdisciplinary cycle, assessments, compliance, quality, scheduling, bereavement, volunteers and staff credentials are all independent of any vendor. Claims are constructed and then held with a note naming the two settings to add, and the same button resubmits them once the account exists. The settings page reads back which credential pairs are present, so the checklist is visible from inside the product.

Why does a quality indicator return nothing instead of zero?

Because zero is a claim. A quarter with no deaths has no rate for visits in the last days of life, and printing nought percent asserts a quality failure the data does not support. Returning nothing lets the page say 'no data', which is what is true. The same rule applies to every indicator in the engine.