Resources & Tech · CCM Solution

Append-only time, coded plans, and a signature that cannot be taken back.

A chronic care management program is an evidentiary exercise as much as a clinical one: what was done, by whom, for how long, and did the patient agree. This module is built so those answers cannot be edited after the fact — time logs are append-only, a signed monthly review is immutable, care plans are built from coded terms rather than free text, and every mutation writes an audit row in the same transaction.

Overview

The engineering record behind enrollment, coded care plans, care-time tiers and an attested monthly review.

Technology stack by layer
LayerWhat runs thereWhy
Care-manager front endNext.js 14 App Router, React 18, TypeScript, Tailwind — a dedicated care-program applicationCare managers do one job all day. They get a purpose-built application rather than a section of the general portal.
State and dataTanStack Query and Zustand, with domain hook modules per surfaceAround twenty-eight hooks for the program itself, plus dedicated modules for care plans, monthly reviews and memos.
ChartsRecharts, for dashboard KPI cards and program trend viewsProgram-level counts only. There are no revenue widgets on this dashboard — that is a locked design decision, not an omission.
APIFastAPI, async throughout, mounted under one program namespaceEvery endpoint is gated by an explicit program permission — read, write or admin — rather than by role name.
DatabaseMySQL 8 with the collation pinned explicitly on every table in this moduleTables created without an explicit collation break cross-table joins against the patient and entity tables. Pinning it is a deliberate defence against a failure mode this codebase has already been bitten by.
MigrationsAlembic, a linear chain with existence guards, several of them data backfills rather than schema changesTwo of the backfills exist because the alert engine shipped with no rules for existing patients — an empty inbox looks exactly like nothing being wrong.
Background jobsAPScheduler — a nightly monthly-cycle computation and a daily compliance scanThe compliance scan is scheduled after the monthly cycle deliberately, so it reads settled data rather than data mid-recompute.
CacheRedis, sixty-second TTL on the program dashboard, per entity and programThe dashboard is the most-hit and least time-critical read in the module.
EMR surfaceA read-only summary card embedded in the patient chartPoint-of-care visibility without a second place to edit. The card says so in its own copy: enrollment and time logging are managed in the program portal.
Architecture

How the engine actually works

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

Enrollment

Five states, one table of legal moves

Enrollment status is a five-value enum — pending consent, active, paused, disenrolled, deceased — with pending consent as the server default. Every legal transition is enumerated in a module-level map: pending consent may become active, disenrolled or deceased; active may become paused, disenrolled or deceased; disenrolled is terminal except for death; deceased is fully terminal.

Care plans

A coded vocabulary, restored on purpose

Care plans are built from exactly four vocabulary types — risk, indicator, goal and action plan. That structure was deliberately restored from an older system's coded-term model after an interim build had reduced care plans to free-text checklists. Free text is faster to ship and impossible to report on.

Time

Written once, invalidated at most, never edited

A care-time log's duration is computed on the server from start and end timestamps, ceiling-rounded to whole minutes, and never accepted from the client. Creation validates that the patient belongs to the caller's entity, rejects future-dated entries and durations over eight hours, and resolves which enrollment the log belongs to server-side.

The monthly review

Three answers, not two

The monthly clinical review is one row per entity, patient and month, enforced by a unique constraint. Two attestation fields were restored via a dedicated migration: whether the plan was reviewed with the patient, and whether the patient confirmed understanding. Both are nullable booleans, and the migration is explicit about why they are not defaulted to false — nobody recorded an answer is a different fact from the answer was no, and every row written before the columns existed is the first case.

Vitals in the review

Compute-vitals writes nothing

The compute-vitals endpoint on a monthly review runs aggregate reads over the month's vital readings — blood pressure, glucose, heart rate, oxygen saturation, latest weight — and joins the most recent completed depression-screening score.

Candidates

A rule you can read, not a score you have to trust

The candidates worklist finds patients by a fixed rule rather than a computed score: query active chronic problems, map each diagnosis prefix to a chronic-condition group, keep patients with two or more distinct groups, exclude anyone with an open enrollment, scope to the requesting clinician's panel unless they hold a cross-panel permission, and exclude deceased patients.

Alerts

Whole-rule resolution, because partial inheritance lies

An alert rule is either an entity default, with no patient set, or a per-patient override. Resolution is wholesale rather than a per-field merge: if a patient rule exists it is used in full, otherwise the entity rule is used in full.

Data model

The records underneath, and the rules that hold them true

Core entities and their invariants
EntityWhat it holdsRule that always holds
ccm_rpm_enrollmentsOne patient's participation in one program, with consent fields, assigned care manager and the qualifying conditions.Status moves only along the enumerated transition map; only one open enrollment per patient and program, enforced in the service layer.
care_time_logsAppend-only clinical time entries with a server-computed duration and an activity type.Clinical fields are never edited. The only mutation is recorded to invalidated, applied as an atomic conditional update.
patient_monthly_reviewsThe clinical monthly record — goal progress, a vital snapshot, two attestations, and a signature.Unique per entity, patient and month. Once signed, update and delete both return a conflict.
ccm_care_plan_vocabularyThe coded risk, indicator, goal and action-plan terms that plans are built from.A null entity means a global term — readable by every tenant, editable by none. Terms are retired, never deleted, so old plans keep resolving.
ccm_care_plansOne structured plan per patient, program and month, referencing vocabulary terms by id.Term ids are validated against their expected vocabulary type at write time. The enrollment link is nullable so a plan outlives a closed enrollment.
ccm_care_plan_templatesTenant-defined reusable term picks keyed to a diagnosis.Tenant-scoped only — there are no global templates. Unique per entity and name.
ccm_alert_rulesThreshold and compliance bounds, either an entity default or a per-patient override.Resolution is whole-row. A patient override never partially inherits from the entity default.
ccm_alertsAlert instances with type, severity and lifecycle status.Deduplicated on entity, patient, type and metric while an open alert exists.
emergency_alertsA separate critical-severity escalation stream with its own acknowledge-and-resolve lifecycle.Queued in-session and dispatched only after the triggering transaction commits.
patient_memosPer-patient follow-up reminders with a remind-on date.Deliberately not folded into clinical notes — a working reminder is not signed documentation. Removal is a soft dismissal.
Interfaces

What it exchanges, and in which direction

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

Shared patient record

Bidirectional

The program reads the same patient, problem and chronic-disease tables the rest of the platform uses. There is no separate care-management patient model to reconcile.

Vitals

Inbound

Latest-vitals lookups run through a dedicated composite index rather than a per-patient scan, because the readings table is large and the worklist reads it on every page load.

EMR encounters

Inbound

The patient overview timeline joins clinical encounters, and the worklist surfaces the most recent completed visit date.

EMR chart card

Outbound

A read-only summary card in the patient chart calls one program endpoint. It is explicitly not an editing surface — the card says where enrollment and time logging live.

Device readings

Inbound

Device-authenticated ingest with bcrypt-hashed keys, rate limiting per device, and idempotency on device and measurement time — a duplicate returns success with a deduplicated flag rather than an error.

In-app notifications

Outbound

Alerts notify the enrollment's assigned care manager in-app on the same transaction as the triggering write. Email and SMS fan-out is deliberately skipped for this path — a no-op when no care manager is assigned.

Patient messaging

Outbound

SMS messaging to a patient's mobile number from the program's own messaging surface.

Security

How access is decided and recorded

Permission gating
Every endpoint requires an explicit program permission — read, write or admin. Admin specifically gates operations that should not be open to every care manager with write access, such as an on-demand monthly recompute.
Role grant correction
A follow-up migration exists because the initial permission grant referenced a role name that does not exist in this system. The clinical read-only roles were granted properly afterwards, and the migration records why.
Entity scoping
Entity comes from the token on every write, never the request body. Cross-entity lookups return 404 rather than 403, so a caller learns nothing from the error.
Global vocabulary is read-only
Editing a global care-plan term returns a clear 403 telling the caller to add their own term instead — shared clinical vocabulary cannot be silently redefined by one tenant.
Device credentials
Device API keys are bcrypt-hashed at rest. The plaintext key is shown exactly once, at provisioning, and never again.
Signed-record immutability
Signing takes a row lock to close a concurrent double-sign race, and every post-signature mutation is rejected with a conflict.
Audit on every mutation
Enrollment, time log, care plan, vocabulary, template, review and memo writes all call the platform audit helper inside the same transaction, alongside any domain-specific signature field.
PHI in logs
The device ingest route is registered as PHI-redacted so its free-text notes field is never written to logs.

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

Atomic conditional update
Invalidating a time log applies only to a row still in the recorded state, in one statement — no read-then-write window for two concurrent invalidations to race through.
Row lock before signature
The monthly review sign path locks the row before checking status, so two simultaneous signatures cannot both commit.
Idempotent device ingest
Readings deduplicate on device and measurement time and return success with a flag rather than a conflict — a device retrying on a flaky connection is a normal event, not an error.
Queue then drain
Emergency alerts are queued on the session and dispatched only after commit, so nothing is escalated for a reading that a rollback removed.
Timezone in one place
The reading-day calculation runs in a fixed program timezone held as a single constant, with a per-entity setting documented as the follow-up. Localising it means that change touches one line, not every call site.
Freeze after a grace window
The prior month is recomputed nightly during a short grace window and then frozen. A frozen summary is never overwritten again.
Heartbeats on every run
Both scheduled jobs write a heartbeat on success and on failure, so the health endpoint can distinguish a job that failed from a job that never ran.
Backfills as a reliability pattern
Two migrations exist purely to seed alert rules for patients and entities that predated the alert engine — an inbox with no rules behind it looks identical to an inbox with nothing wrong.
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 CCM Solution
DocumentKindDateWhat it covers
CCM/RPM module specification.planning/milestones/CCM-RPM-MODULE-SPEC.mdDesign2026-06-10The original full module spec — dashboard, enrollment, monthly cycle, alerts and emergency alerts — modelled on an older care-management portal's feature set and rebuilt on the entity graph.
Enrollment core phase.planning/phases/99-ccm-rpm-enrollment-core/Implementation2026-06-10A full plan-execute-verify cycle for enrollment alone — the state machine, eligibility computation and the identifier-probing mitigations — with research, patterns, security, review, validation, verification and user-acceptance artifacts.
Time tracking phase.planning/phases/100-ccm-rpm-time-tracking/Implementation2026-06-10The append-only time log and tier computation build, scoped in its own context document as compliance only, explicitly not billing.
Vitals and monitoring foundation phase.planning/phases/11-vitals-rpm/Implementation2026-05-29The earlier readings and thresholds foundation that compute-vitals and the alert engine were later built on, including its discussion log and deferred-items list.
Care-program service plan.planning/milestones/RPM-SERVICE-PLAN.mdPlan2026-08-19The plan for the standalone care-program operations service, superseding the earlier spec's front-end portion and reorganising the backend work into build phases.
Service plan companion analysisdocs/rpm-service-implementation.htmlAnalysis2026-08-19A diagrammed companion to the service plan, visualising the same architecture for review.
Post-implementation open items audit.planning/milestones/RPM-SERVICE-OPEN-ITEMS.mdAnalysis2026-08-24A line-by-line audit of what the service plan promised against what actually shipped in the following week — including the alert-rule seeding gap that the two backfill migrations later closed.
Care-program QA design.planning/qa/2026-08-25-ccm-rpm-qa-design.mdQA2026-08-25A manual QA plan across the care-program application's screens, which also documents the module's automated-test tier gap in the team's own words.
QA run screenshots.planning/qa/run-2026-08-25/shots/QA2026-08-25Captured screens from that QA pass, including the program dashboard and patient detail views.
Point-of-care module documentationdocs/docs-module-emr-ccm.html · docs/docs-module-emr-rpm.htmlReference2026-06-04Written documentation for the chart-embedded care-program surfaces.
Care coordination, patient and alerts module docsdocs/docs-module-{care-coordination,patient,alerts}.htmlReference2026-04-27Written documentation for the care coordination, patient management and alerts modules the programme builds on.
Glossary

Terms used on this page

Chronic care management
A program for patients with two or more active chronic conditions, tracked through enrollment, a coded care plan and monthly clinical time.
Enrollment
One patient's participation in one program, governed by a five-state lifecycle.
Care-time log
An append-only record of one clinician's time on one patient's care in one sitting.
Compliance tier
A bucket summarising month-to-date recorded minutes, shown to care managers. Distinct from any billing derivation.
Care-plan vocabulary
The coded risk, indicator, goal and action-plan terms structured plans are assembled from.
Care-plan template
A tenant-defined reusable set of vocabulary picks keyed to a diagnosis.
Monthly review
The clinical summary, goal progress, vital snapshot and attestations for one patient in one month — distinct from the monitoring compliance summary.
Attestation
A tri-state record — yes, no, or unanswered — of whether the plan was reviewed with the patient and whether they confirmed understanding.
Signed
The one-way transition of a monthly review to an immutable state. Update and delete are refused afterwards.
Tri-state
A field that distinguishes false from unanswered, rather than defaulting silence to a negative.
Alert rule
Threshold and compliance bounds, scoped either to an entity as a default or to one patient as an override.
Whole-row resolution
Using the patient override in full or the entity default in full, never merging fields from both.
Emergency alert
A separate critical-severity stream with its own acknowledge-and-resolve lifecycle, dispatched after commit.
Patient memo
A working follow-up reminder, deliberately kept separate from signed clinical documentation.
Candidates worklist
The rule-based list of eligible patients not yet enrolled.
Month to date
The rolling window recorded minutes are summed over for the current month.
Frozen month
A monthly summary past its grace window, no longer recomputed by the nightly job.
Backfill migration
A one-time idempotent data migration that fills a gap left by a feature shipping after the rows it needed already existed.
Questions

Asked by the people who evaluate this

Why can't a care manager correct a time entry?

Because a correctable time record is a weaker record. The entry can be invalidated with a stated reason and a new one written, which leaves both the mistake and the correction visible. Editing in place would leave only the second version, and the difference between those two designs is exactly what an audit is looking for.

What does a tri-state attestation actually buy you?

It stops the system manufacturing evidence. If the field defaulted to false, every review written before the question existed would assert that the plan was not reviewed with the patient — a claim nobody made. Nullable means the record can say 'no answer was recorded', which is the truth.

Why whole-rule resolution instead of merging patient overrides onto entity defaults?

A merge produces rules nobody wrote. Set an upper bound for one patient and a field-level merge silently pairs it with a lower bound from the entity default — a combination no clinician reviewed and no document describes. Whole-row resolution guarantees the rule in force is one a human authored end to end.

Does this tell us what a program will be paid?

No, and it deliberately does not try. The compliance tier shown to care managers summarises recorded minutes into a bucket; the module that derives billing line items is separate code, and nothing on this page states or implies a reimbursement outcome. Payment depends on payer, contract and program rules that are not ours to promise.

Can we edit the coded care-plan vocabulary?

You can add and edit your own terms and build your own templates. Global terms — the shared ones every tenant reads — are read-only, and attempting to edit one returns a message telling you to add your own instead. That keeps a shared clinical vocabulary from being quietly redefined under one tenant's feet.