Appearance
Gaps found while verifying the guide
Patient records review — 2026-09-25
See the browser review and proposed workflows for the original evidence and FAC-REC-01 for the implemented workflow and current limits. Waitlist is excluded; Epic is the workflow case study.
- Fixed locally · Historical notes and closed-visit review (REC-001/002). Legacy signed notes display with provenance; closed visits use a read-only view with explicit addenda. The API refuses ordinary note, vital and new-order writes to a closed visit while preserving late results and active admission care. Provider workflow walked in Chromium.
- Partly addressed · Portability (REC-004). Reviewed V2 JSON and offline HTML exports include clinical sections, attributed note versions/addenda and original chart attachments. A 206-visit API test proves the old cap is removed. The preview lists excluded records. Automated import into another EHR, a standardized transfer package and omitted specialty/MAR/imaging content remain open.
- Partly addressed · Release controls (REC-005). Review-state gating, preview, download and permission boundaries are implemented. Granular scope selection, structured recipient authorization, secure delivery and acknowledgements remain open.
- Local workflow implemented · Outside records (REC-006). One-use, expiring patient-bound links feed clinical review; acceptance files the original with source and reviewer provenance. Referral and non-referral requests are explicit, with an optional referring-organization reference retained in the chart and export. Neither requires an open visit. Recipient-email codes and private ClamAV scanning now guard new submissions, with durable per-link code limits and no download or acceptance without clearance. Mailbox access does not establish organizational identity. Public rollout still needs operational abuse/retention/storage controls, signature-update monitoring, deployment verification and organization verification. A lab PDF is an attachment; structured incoming lab results need a separate matching and verification workflow. See operator setup and the current browser evidence in FAC-REC-01.
- Local workflow implemented · Incoming queue (REC-007). Results inbox now lists patient-scoped pending outside records oldest first, with exact counts, pagination, reviewer claims, own/unassigned filters, and a visible indicator after 24 hours. Waiting for you links to the queue. Supervisor reassignment now requires a reason, preserves handover history and validates recipient access; stale forms cannot overwrite a newer claim. Notification polling creates assignment alerts, daily reminders after 24 hours, and supervisor escalations for aged work or an unavailable reviewer. Obsolete alerts are removed from active lists/counts. Independent background scheduling, configurable review deadlines and leave-roster integration remain open.
- Local workflow implemented · Filing corrections (REC-008). A clinician can withdraw an accepted outside attachment with a reason while preserving its original bytes and acceptance history. Generic destructive removal is blocked. Current attachment lists, ordinary downloads and new exports exclude the withdrawn document. Exact-file duplicate checks and clinician-confirmed replacement links are now implemented. Replacement filing and withdrawal of the earlier attachment commit together; original bytes and acceptance history remain stored, and new exports include only the current version. Semantic duplicate matching, historical-file viewing and recipient correction notices remain open; existing downloaded copies cannot be recalled.
- Verification gap · Firefox. Playwright Firefox cannot launch on this Mac: macOS denies its plugin-container sandbox extension and the compositor fails. Both agent and normal-Terminal attempts fail before visiting VERA; FAC-OPS-03, FAC-GOV-01, FAC-GOV-02 and FAC-GOV-03 attempts also failed during launch before the page opened. On 30 September the user approved Chrome as the fallback. Installed Google Chrome passed the complete records workflow, including duplicates and replacements, on an isolated fresh demo; Firefox support itself remains unverified.
Every journey in this guide is walked in a real browser before it is published (see capture/). When a step can't be completed, or behaves differently from what users are told, it is logged here instead of being written around. The guide describes what the product does today. Each entry says what the guide tells users to do in the meantime.
Walk of 2026-09-23. Control plane: local platform-api and platform-admin on a local Postgres. Facility: demo tenant (demo-reset.sh + seed-demo-environment.mjs, "Nothing skipped"), facility-web production build.
Department workflows — 2026-10-09
FAC-WF-01 … FAC-WF-04 walked on an isolated, freshly seeded stack (GUIDE_STACK=workflows, Nothing skipped) as the Facility Admin, Dr. Ngozi Okafor and Dr. Tunde Balogun (General Outpatients reviewers), Sr. Chioma Eze (assistant), Front Desk and the Accreditation Coordinator, in Chromium and installed Chrome. FAC-WF-04 used a local knowledge service with a synthetic, hospital-owned protocol. All wording is synthetic; no clinical approval was recorded.
Found and fixed while walking:
- Fixed · Stylus strokes could be lost. The pad called
setPointerCaptureunguarded; when the browser had already released the pointer it threw and the stroke never started. Capture is now best-effort. - Fixed · Extraction check looked unresponsive. The tick box waited for a server reply before showing as ticked; it is now saved with the other source checks.
- Fixed · Review dates shifted by a day. A date typed as 30/06/2027 was stored as end of day in the browser's time zone and shown as 01/07/2027 in the facility's. Source review dates and role end dates are now stored as the facility day.
- Fixed · Repeated cosign instruction and an overflowing checksum on the run and source-review pages.
- Fixed · Raw codes on the discharge acknowledgement (FAC-IPD-01). Re-walking discharge with the shared signature pad showed ON_SCREEN, PATIENT_RECORD, IN_PERSON and ACKNOWLEDGED; they now read On-screen signature, Matched to patient record, In person, Acknowledged. FAC-IPD-01 now signs on screen.
Open:
- Medium · Real-device signing not proven. Mouse drawing is walked in both browsers; pen input and an ignored resting second touch are exercised with synthetic pointer events. Finger and stylus on real phones and tablets, orientation changes and palm rejection need a device pass.
- Medium · No reminders or escalations for review queues. Queues show overdue items and claims expire after 30 minutes, but nobody is notified (GOV-17).
- Medium · No "not applicable" with reason, evidence attachments or issue tracking on review items (GOV-16, GOV-18 partial).
- Low · Pending work is not rerouted when a department is retired or restructured (GOV-06); retired departments refuse new starts and appointments.
- Low · Signed workflow records are not yet in the chart export or PDF; the run page prints.
- Verification gap · Firefox not attempted (owner chose Chrome, 2026-10-08).
Vera Chat — 2026-09-30
Walked in Chromium as Sr. Chioma Eze (nurse), Dr. Ngozi Okafor, Dr. Tunde Balogun and Dr. Hauwa Ahmed (providers) on a fresh demo seed; capture/chat.spec.ts passes. The walk found and fixed three bugs before publication: the chart's Team tab (the first home of "Chat with team") is missing from the closed-visit view, so the entry point moved to the patient header; the composer's close handler raced its navigation and dropped the new conversation; and a colleague pulled in by auto-forward was alerted to inherited history.
- Live push added (FAC-CHAT-03). Server-Sent Events from the tenant server push messages, typing, receipts and badge changes. Browser desktop alerts work while Verabase is open in some tab. Its walk found and fixed three bugs: a remounting stream could show Live while no stream was subscribed; a message sent while a page reloaded raised no alert (now caught up on reconnect); and a status-dialog bug: opening Your status before it had loaded let the saved status overwrite your edits, so a doctor setting themselves back to Available stayed Away.
- Low · Web Push walked by tests only. Closed-app alerts now use Web Push with our own VAPID keys, carrying no message or patient. Server tests check the signature, the push-service allowlist (Google, Mozilla, Apple, Microsoft only), throttling, mute and Do-not-disturb rules, and removal of gone subscriptions. A real browser walk needs a live push service, which headless test browsers lack; check it once on a real device. There is still no native mobile app.
- Low · Catch-up alerts are high priority only. When the stream reconnects (after a page load or a network drop), the tab raises desktop alerts for high-priority messages that arrived while it was down; normal messages only update the badge. Catch-up compares against this computer's clock, so a badly wrong clock can miss or repeat one.
- Low · Single process. The event hub is in memory; it relies on the one-tenant-server-per-tenant rule. Running two instances for one tenant would need a shared bus.
- Walked: FAC-CHAT-04 to 09 cover groups from On duty now, photos, PDFs, rename, search, mute, mark-as-unread, leaving, @mentions, edit and recall, adding people (including one-to-one to new group), attaching a patient later, forwarding, chat records, video calls through a relay, and outside clinicians.
- Low · Not built: group (3+ person) calls, screen sharing, emailing outside clinicians when a new message arrives (they must open their link), attachments from outside clinicians, and a native mobile app. Calls are one-to-one by design for now.
- Medium · Relay deployment not yet live. coturn and the Caddy layer-4 overlay (
cloud-platform/infra/docker/docker-compose.turn.yml) are written and checked locally. The stock Caddyfile still validates after the split, the layer-4 Caddy image builds,Caddyfile.turnvalidates, and coturn starts with the shipped configuration. FAC-CHAT-08 proved a call relayed through a local coturn over TCP. Not yet exercised: TURN over TLS on :443 through Caddy on the real host. Until the relay is deployed, calls connect only where the two browsers can reach each other directly. - Verification gap · Firefox. Not run for this change; see the records entry above for why Playwright Firefox cannot launch on this Mac.
Severity
- High: clinical safety or a step that cannot be completed.
- Medium: misleading to the user, or needs a workaround.
- Low: polish.
Facility (hospital system)
✅ Fixed · Dedicated lobby display accounts could not finish sign-in
Found while walking FAC-PAT-01, 2026-10-02. The lobby role had only access.queue.display, but runtime bootstrap also reads facility settings behind core.dashboard.view, leaving the account on the login page with a permission error. Facility settings reads now allow the kiosk and display permissions; writes remain admin-only, and a server test confirms unrelated roles are still refused. FAC-PAT-01 passed in fresh-seed Chromium after the fix.
✅ Fixed · Clinicians cannot record allergy status, and the failure is silent
Fixed 2026-09-23. New permission
access.patients.allergies.record, granted to Provider, Nurse and Pharmacist. It is accepted on add flag, remove flag and set allergy status, but not on demographic edits. The chart and profile show the controls to holders. Covered byclinician-allergy-recording.test.tsand walked in FAC-IPD-01 step 2.
- Where: chart → Flags → No known allergies (
facility-web/lib/patient-flags.tsx); serverPUT …/access/patients/:id/allergy-status(verabase-tenant-server/src/app.ts:2395). - What happens: the endpoint requires
access.registerPatient. Front Desk and Facility Admin get 200. Provider, Nurse and Pharmacist get 403 FORBIDDEN. The button is still shown to them, and nothing visible happens when they click it. - Why it matters: the doctor is exactly the person who asks "any allergies?". Allergy status is the input to the allergy gate.
- Fix: gate on a clinical permission (e.g. a
clinical.recordAllergies) held by provider and nurse, and render the mutation error the component already captures.
✅ Fixed · Non-critical results never reach the Results inbox, and there is no acknowledge action
Fixed 2026-09-23. The backend queue and acknowledge already existed, but only the Lab page showed them. "N results to review" linked to the Results inbox, which didn't. The queue is now a shared
ResultsReviewCardshown at the top of Results inbox (and on Lab). Walked in FAC-OPD-01 step 8.
- Where: Clinical → Results inbox (
/clinical/inbox); chart → Labs. - What happens: a verified FBC with WBC and neutrophils flagged High shows on the chart as New result, but not in the ordering doctor's inbox (Unread or All activity). The chart has no Acknowledge. The lab bench still tells staff that step 5 is "Acknowledge in the inbox".
- Why it matters: the result-review loop never closes for abnormal-but-not-critical results.
WALKTHROUGHS.md§1.6 says the opposite. - Guide says for now: the doctor reviews results on the chart's Labs tab.
- Fix: notify the ordering clinician for every verified result (or at least abnormal ones), and add acknowledge-with-note on the chart and in the inbox.
✅ Fixed · Doctors cannot request an admission bed in the UI
Fixed 2026-09-23. The placement request form, patient search and source-visit lookup now render for
inpatient.admissions.request. Allocation and admission stay withadmissions.manage. Walked in FAC-IPD-01 step 2: the doctor requests, the nurse allocates.
- Where: Bed board → Placement requests (
facility-web/app/(app)/inpatient/page.tsx, form gated oncanAdmit=inpatient.admissions.manage). - What happens: providers hold
inpatient.admissions.request, and the server accepts theirPOST …/bed-requests(app.ts≈5611). But the form only renders foradmissions.manage, so a doctor sees the placement queue with no way to add to it. The designed split ("doctor decides, ward places") only works if the nurse raises the request. - Guide says for now: the doctor documents the decision to admit, and the nurse raises the request (FAC-IPD-01 steps 2–3).
- Fix: render the request form for
requestAdmissiontoo, and add a Request admission action on the chart.
✅ Fixed · Discharge doesn't route the bed through cleaning; ward nurses can't set bed status
Fixed 2026-09-24. Discharge now sets the bed to Cleaning ("Discharge turnaround") and raises the bed-turnaround housekeeping task. Discharge is never blocked by housekeeping. Completing that task (with a note) makes the bed Available. Covered by
app.test.ts(discharge) andhousekeeping.test.ts(release), and walked in FAC-IPD-01 step 9.
- Where: discharge → bed tile.
- What happens: after discharge the bed is immediately Available. Cleaning, and the housekeeping task it creates, only happen if someone with
inpatient.beds.manage(Facility Admin in the demo) sets Set status → Cleaning. The nurse who discharged the patient can't.WALKTHROUGHS.mdPart 4 step 7 says discharge makes the bed cleaning with a housekeeping task. - Fix: move a discharged bed to Cleaning automatically (creating the housekeeping task), or give ward nurses that one transition.
✅ Fixed · Housekeeping could not finish sign-in or review completed work
Fixed 2026-10-03. Runtime bootstrap now lets Housekeeping read facility settings through its housekeeping permissions; unrelated roles remain refused and writes stay admin-only. The completed worklist row displays the saved completion note, and its instructions say that completing a turnaround returns the bed to Available. The settings boundary is covered by
app.test.ts. The inpatient capture passed 12 workflow checks on a fresh guide seed in Chromium, as Amaka Okafor (Housekeeping) and the Nurse; screenshots were inspected and retained inassets/fac-ops-03/.
- Where: sign-in runtime bootstrap and Inpatient → Housekeeping → Worklist.
- What happened: Housekeeping had the permissions to manage turnaround work, but the bootstrap settings read refused the role before sign-in could finish. After a task was completed, the worklist hid the saved completion note and its footer said bed availability needed separate manual release.
- Fix: allow the settings read for housekeeping view/manage permissions without granting dashboard access, render saved completion notes on completed rows, and describe the automatic bed release accurately.
✅ Fixed · Audit log actor filter required a staff UUID
Fixed 2026-10-03. The audit log now filters by a named staff member, with the email shown to distinguish people with the same name. Facility Admins get the full staff directory; other audit viewers get names from the events already in view. On a fresh guide seed, FAC-GOV-01 opened Aisha Garba's chart as Nurse Sr. Chioma Eze, found the patient-specific read in the Admin log, and verified the filtered CSV export.
- Where: Administration → Audit log → Staff member.
- What happened: the actor filter asked the Facility Admin to paste a staff ID, but the UI did not show those IDs anywhere. A compliance review couldn't be completed from names or other information visible on screen.
- Fix: show staff names and email addresses in a selector while keeping the actor ID internal to the filter request.
✅ Fixed · Retention workflow exposed patient UUIDs and raw review states
Fixed 2026-10-03. FAC-GOV-02 now uses patient search for patient-scoped holds and disposal reviews, shows plain-language scope and decision states, and makes action errors visible. On a fresh guide seed, Facility Admin placed a chart hold, saw the matching disposal review refused, released the hold with a note, and recorded an approved human review. The inspected screenshots are retained in
assets/fac-gov-02/.
- Where: Administration → Retention & legal holds.
- What happened: patient-scoped actions required pasting an internal patient UUID, and the review queue displayed the raw
PENDINGenum. - Fix: use the patient search picker, describe scope without exposing IDs, and render human-readable decision labels.
✅ Fixed · Notifiable register did not open its source
Fixed 2026-10-04. FAC-GOV-03 passed on a fresh guide seed in Chromium as Dr. Tunde Balogun (Provider) and Facility Admin. The register now links to the exact active or cleared infection-control entry, with plain-language status. The source retains the condition, reason, notification reference and recorded/cleared author and time. The two infection-control integration tests passed; the browser walk verified filtering, both source links and CSV provenance. All four final screenshots were inspected.
- Where: Reports → Notifiable disease register and the patient's Infection prevention & control panel.
- What happened: the register said rows linked to the source, but displayed an internal table/UUID string without a link. The source view also hid the notification reference and reduced cleared history to a time and author.
- Fix: link to the matching source entry, scroll it into view, retain reconciliation details in history, and replace raw states and precaution enums with readable labels.
✅ Fixed · Ward MAR omitted a first dose due across shift handover
Fixed 2026-10-02. The named-shift round now includes medication windows that overlap the shift. A first dose scheduled just before handover remains visible as Due with its Record action for the incoming nurse. Covered by
medication-shift-scope.test.tsat the 07:00 WAT boundary and walked in FAC-IPD-01 step 6 as the ward nurse.
✅ Fixed · Front-desk search does not match a full name
Fixed 2026-09-24. Search splits the query into words and every word must match a name, MRN or phone (in any order). Identifiers still match the whole term. Covered by
patient-search-words.test.ts, and walked at check-in (FAC-OPD-01 step 3) and in 3c.
- Where: Front desk → Search cached patients.
- What happens:
Nwachukwufinds Sani Nwachukwu;Sani Nwachukwureturns "No patients match this search. Register them using the form on the right." That invites the exact duplicate the register then has to catch. - Fix: tokenise the query and match all tokens across given and family name.
✅ Fixed · Two prescription composers with different safety UX
Fixed 2026-09-24. The quick composer now shows the allergy match and a Reason for prescribing anyway field when the server refuses. Prescribe over allergy warning stays disabled until the reason reaches the shared 10-character minimum, and the order carries the Allergy override badge. Walked in FAC-OPD-01 step 6.
- Where: chart → Orders → Prescription (quick) vs chart → Prescriptions (full,
lib/chart-prescribing.tsx). - What happens: the quick composer shows the server's allergy refusal "Give a reason to prescribe it anyway" but has no reason field. Only the full composer shows the match detail and takes an override reason, and it also runs duplicate-therapy, dose and high-alert checks.
- Fix: give the quick composer the same allergy panel and override input, or send prescriptions from Orders to the full composer.
✅ Fixed · The registration form's "Known allergies" box does not set allergy status
Fixed 2026-10-01. Registration has a structured Allergies section: Not asked yet (the default), No known allergies, or Has allergies, with a substance, reaction and severity (Mild / Moderate / Severe) for each. The answer is written to the allergy record the prescribing check reads, marked Reported at registration by <name>, and the banner says reported at registration… Confirm with the patient. Free-text allergy questions in the question bank no longer show at the desk; the patient questionnaire keeps its own. Covered by
clinician-allergy-recording.test.ts(each answer, the allergy gate refusing amoxicillin, a role without registration or allergy permissions refused) andregistration-allergies.test.ts. Walked in FAC-OPD-01 steps 1–2 and 3d as Front Desk and Provider.
- Where: Front desk → Register patient → Known allergies.
- What happens: typing "No known drug allergies" saves free text to the profile, but the chart banner still reads Allergy status not recorded — nothing to check against.
- Fix: either map the answer to a structured allergy status or flag at registration, or label the box as notes only.
✅ Fixed · Chart prescriptions don't reach the pharmacy until an admin turns on "Provider orders"; demo data leaves it off
Fixed 2026-09-24 (demo).
seed-demo-environment.mjsturns Provider orders on, and FAC-OPD-01 step 0 now checks it arrives on. Real hospitals still set it at go-live (see the catalogue's go-live checklist).
- Where: Pharmacy → Prescribing integration (admin only).
- What happens: with both integrations off (the demo default), a doctor's prescription never appears in the dispense queue. The pharmacist sees a warning but can't change the setting.
- Guide says for now: FAC-OPD-01 step 0.
- Fix: turn Provider orders on in
seed-demo-environment.mjs, and add it to the go-live checklist, or default it on when Pharmacy is granted.
✅ Fixed · Billing: no patient names in the invoice list; "Patient ID" and kobo fields
Fixed 2026-09-24. Invoices carry
patientName, shown on each row and in the invoice detail. The Patient ID fields (estimates, account statement, deposits, prior authorisation) use the patient picker. Every money field takes naira, labelled "(₦)", and converts to kobo on submit (nairaInputToMinor, tested). Walked in FAC-OPD-01 step 10. Still raw: deposit and estimate lists show a patient id rather than a name.
- Where: Revenue → Invoices & payments.
- What happens: invoices are listed as INV-000065 · Draft ₦9,000, with no patient. The Patient account statement, Deposits and Prior authorization panels ask for a raw Patient ID. Payment and invoice amounts are entered in kobo (₦9,000 is
900000). - Fix: show the patient on each invoice row, use the patient search picker everywhere, and take naira in the input.
✅ Fixed · Outpatient take-home prescriptions appear on the ward drug round
Fixed 2026-10-01. A prescription is take-home when it is written outside an inpatient, emergency or day-case visit, or is discharge medication. A take-home course gets no doses on the drug chart. The only exception is a first dose the prescriber asks the clinic to give: Give the first dose here in clinic, unticked by default on clinic visits. Resuming, re-timing or topping up a take-home order adds nothing. The dispense is the course (TDS × 5 days = 15), not a count of chart boxes, which had given 16. MAR tests that prescribed for patients who were never admitted now admit them first. Covered by
take-home-medication.test.ts. Walked in FAC-OPD-01 steps 3, 3e and 6 (Provider, Nurse, Pharmacist); FAC-MAR-01…03 still pass.
- Where: chart → Orders → Prescription. Give the first dose at the next round now is ticked by default.
- What happens: Adaeze's take-home amoxicillin appears on the MAR round as a due dose with Take this patient, although she is an outpatient. The dispense quantity was 16 for TDS × 5 days (15 doses).
- Fix: default the checkbox off for outpatient visits, and confirm the quantity rule.
✅ Fixed · Profile header stays stale after recording "No known allergies"
Fixed 2026-10-07. The profile header read flags under its own cache key, so the Flags panel never refreshed it. It now shares the panel's key. Walked in FAC-OPD-01 step 3c: Front Desk adds an allergy and the header shows Allergies: Penicillin (HIGH) without leaving the page.
The patient profile header keeps saying Allergy status not recorded after a successful save, until navigation. The chart banner is correct.
✅ Fixed · A scanned chart document hid who added it
The API recorded the staff member who attached a patient document, but the chart list showed only its type, file details and date. The row now identifies who added the document. FAC-OPD-03 walks a referred arrival from registration through the Front Desk scan and Provider review on a fresh seed; capture/referral-in.spec.ts asserts the attribution is Blessing Umar in both roles.
✅ Fixed · Vitals have no time-of-service field
Fixed 2026-10-07. The chart's vitals form has Time taken (leave blank for now), read on the facility's clock. The saved row shows the time taken; entry time is still kept separately. Walked in FAC-OPD-01 step 2 as the Nurse (vitals taken 20 minutes before charting).
Notes have Time of service / observation; vitals don't, and are stamped with entry time. WALKTHROUGHS.md §1.3 describes a time-of-service field on vitals.
✅ Fixed · Allergy severity is free text
Fixed 2026-10-07. Add flag → Severity is a fixed list (Mild / Moderate / Severe, stored as LOW / MODERATE / HIGH), the same as registration. Walked in FAC-OPD-01 step 3c.
Add flag → Severity is a text box (placeholder "e.g. Severe"), while seeded allergies read HIGH. Use a fixed list so severity can drive alerting.
✅ Fixed · Copy and display polish
Fixed by 2026-10-07. Lab and pharmacy rows say just now, and the controlled-drug register shows the location's name (or its cupboard), not the raw key. Seen in FAC-MAR-02 and FAC-RX-02.
- Lab and pharmacy rows say "just now ago".
- The controlled-drug register shows raw location keys (
WARD:23b52378-…).
Daily clinical journeys (found writing FAC-OPD-02, FAC-ED-01, FAC-RAD-01, 2026-09-25)
✅ Fixed · Emergency board closed visits around the discharge checks
Fixed 2026-09-25. Every board row had one-click Discharged and Transferred buttons. They closed the emergency encounter with no return precautions and no accepting clinician, which are exactly what End the visit insists on. The reverse was broken too: recording a disposition on End the visit did not take the patient off the board or close the visit, so properly discharged patients stayed on the board. Now the board has one End visit link that opens End the visit with the patient and visit already chosen. Recording the disposition updates the board and closes the visit (an admission still does this through the bed board). The server refuses to set Discharged or Transferred directly (
ED_DISPOSITION_REQUIRED). Covered bytriage.test.tsanded-disposition.test.ts, walked in FAC-ED-01.
✅ Fixed · Clinic times read in the workstation's timezone
Fixed 2026-09-25. The appointment form (booking, waitlist, reminder and message send times) read times in the browser's timezone, while the schedule shows the facility's. Booking 09:00 from a computer set to another zone stored 15:00. Five other forms (radiology and procedure scheduling, MAR downtime observed at, incident occurred at, mortality time of death) filled their time fields from UTC but saved them as facility time, so on a Lagos clinic each default was an hour early. All now read and fill in facility time. Walked in FAC-OPD-02 from a workstation outside Lagos. The demo seed also placed "this morning's" appointments on the seed machine's clock; it now uses the clinic's.
✅ Fixed · Resuscitation copy implied closed logs could still be amended
Found while walking FAC-ED-02, 2026-10-02. The page said corrections could be added as further entries, but its event controls disappear after closure and the server refuses writes to a closed record. The page now says entries are added while the resuscitation is running and the log is read-only after closure. The fresh-seed browser capture checks the wording and verifies the closed log has no event controls.
✅ Fixed · Appointment clinician list held every member of staff
Fixed 2026-09-25. The front desk could book a patient "with" the cashier or the pharmacist. The staff directory now says who sees patients (signs clinical notes), and the picker lists only them. Walked in FAC-OPD-02.
✅ Fixed · Imaging orders didn't appear on the chart until reload
Fixed 2026-09-25. After Place imaging, the chart's Imaging orders panel still said No imaging orders for this visit until the page was reloaded. Every other order panel refreshed. Walked in FAC-RAD-01.
✅ Fixed · Emergency board had no way to start triage, and "Recently ended" named nobody
Fixed 2026-09-25. The board had no Triage a patient button, only a menu item under Clinical workspaces. Row buttons read Waiting and Treatment Area, like statuses, and are now Back to waiting and Move to treatment area. Waiting time was minutes since triage, even for patients being treated, and now reads Since triage. End the visit → Recently ended listed dispositions without the patient's name.
✅ Fixed · Codes shown instead of words (schedule, reminders, radiology, inbox)
Fixed 2026-09-25. Codes that were showing on screen, and what they show now:
- Schedule: FOLLOW_UP is now Follow-up. The double-booking message gave a raw UTC timestamp ("around 2026-09-28T08:00:00.000Z") and now reads "…already has an appointment at that time. Choose another time or clinician."
- Reminders: QUEUED is now Waiting to send. A refused reminder showed its message at the top of the page, off-screen; it now shows in the reminders card, which also reports what Dispatch due sent ("Nothing is due yet…").
- Radiology: ORDERED, XRAY, NOT_APPLICABLE, CHEST XRAY and the section names lungs pleura, bones soft tissues now read in plain words, in the worklist and in the order composer.
- Results inbox: IMAGING is now Imaging, CRITICAL VALUE is now Critical finding for images, and notification types such as CRITICAL_RESULT now read Critical result.
labels.test.tspins every one of these codes to a label.
Fixed, not walked · Mortality review "action due" is sent without a timezone
Fixed 2026-10-07, not yet walked. The card shows and saves Action due on the facility's clock through the shared facility-time helper (
useFacilityDateTime), both ways. Before, it showed the stored UTC value as if local and saved local text as UTC. No journey covers the mortality review yet.
- Where: mortality review card, Action due (
lib/mortality-review-card.tsx). - What happens: the value is sent as the raw
datetime-localtext, so the server reads it as UTC, an hour off in Lagos. - Fix: convert with the facility-time helper like the other forms.
The drug round (found writing FAC-MAR-01…03, 2026-09-25)
✅ Fixed · Random sign-outs: a failed page load revoked the session
Fixed 2026-09-25. When facility-web loaded, any failure fetching the runtime summary signed the user out and revoked the session on the server. That included a request cancelled by clicking a link while the page was loading, a timeout and a 5xx. A token refresh that failed on the network did the same. In the walk this signed the nurse out mid-round about two runs in three. Now only a real credential rejection (401/403) ends the session; any other failure keeps it and retries every few seconds. Pinned by
lib/auth-errors.test.ts, and the FAC-MAR-02 step 5 walk passed 5 of 5 after failing 2 of 3.
✅ Fixed · Dose refusals were invisible on the ward round
Fixed 2026-09-25. The dose form sent server refusals to a message area that only the prescribing form displays. On the ward round, a wrong witness password, a barcode mismatch, a PRN given too soon, or an unverified dose under Block just did nothing when the nurse pressed Confirm. Refusals now show inside the dose form, and other round errors above the round.
✅ Fixed · Controlled-drug register: wrong units, raw ward IDs, no way to count a ward cupboard
Fixed 2026-09-25. Each dose given on the MAR took "1" out of the register but labelled it with the dose unit ("−1 mg"), against a receipt of "20 mg" that was really 20 ampoules. A register's unit is now set by its first entry, and every later movement uses it ("−1 ampoule · balance 18 ampoule"). The demo receipt is 20 ampoules. Cupboards showed as
WARD:<id>, and the pharmacist's free-text Location defaulted toPHARMACY, so a wastage or count against a ward cupboard couldn't be recorded. Location is now a list (pharmacy store plus each cupboard), rows show ward names and who witnessed, movement types read Received / Given on the MAR / Wasted, and the drug name suggests the names already in the register. Walked in FAC-MAR-02.
✅ Fixed · Pharmacist verification couldn't be set to Block
Fixed 2026-09-25. The server supported Warn/Block, and the default is Warn so wards aren't locked out before the pharmacy staffs verification, but facility-web had no control for it. Pharmacy → Prescribing integration now has it. Under Warn the dose form itself also says Not yet verified by a pharmacist, not only the round list. Walked in FAC-MAR-02 step 5.
✅ Fixed · Chart said "Withheld" for a refused dose; paper and unscanned doses looked scanned
Fixed 2026-09-25. Any not-given dose showed as Withheld on the chart and the round, with the reason in small print. The chart and round now show what the nurse recorded (Refused, Not available, Patient absent, Self-administered). Doses copied from a paper MAR are marked from paper (the reference on hover), and a given dose without a scan is marked no scan, as the product's documentation already claimed. Reason codes (
PATIENT_REFUSED,NPO,VOMITED_DOSE) now read in words, and SLIDING SCALE plan now reads Sliding scale plan.
✅ Fixed · Paper reconciliation demanded live barcode scans
Fixed 2026-09-25. Reconciling a dose from a signed paper MAR required scanning the wristband and the medicine now, for a dose given hours earlier. That pushes nurses to fake scans. Paper reconciliation now skips the scans and records the dose as unscanned, with the paper reference. The five rights and any witness still apply. Walked in FAC-MAR-03.
✅ Fixed · Printed drug chart said "generated … 12:00:00 AM"
Fixed 2026-09-25. The print showed the start of the day it covered as its generated time, in US format, and headed the patient Ward Surgical Ward B. It now shows when it was printed, in clinic time (e.g. 25/09/2026, 18:57), and the ward name once.
✅ Fixed · Demo PRN morphine couldn't be given in a demo
Fixed 2026-09-25. The seed gave the PRN morphine 3 hours before the demo, inside its 4-hour interval, so the next dose was always refused. It is now seeded 5 hours back.
✅ Fixed · The drug chart's subtitle is the patient's phone number
Fixed 2026-10-07. Removed; the patient banner above the card already names the patient. FAC-MAR-03 asserts the card title reads only Drug chart.
On the patient MAR, under Drug chart, the patient's phone number appears with no label. It reads as a chart reference. Either label it or remove it.
✅ Fixed · Register units don't pluralise
Fixed 2026-10-07. −1 ampoule · balance 18 ampoules. Word units take an "s"; abbreviations (mL, mg) don't (
lib/counted-unit.ts, tested). Walked in FAC-MAR-02.
"+20 ampoule", "balance 18 ampoule". Cosmetic.
Pharmacy and stock (found writing FAC-RX-02, FAC-INV-01, 2026-09-26)
✅ Fixed · A rejected prescription stayed in the dispense queue
Fixed 2026-09-26. Rejecting an order held it and held its doses, but its pending dispense stayed in the pharmacy queue, so the rejected drug could still be handed over at the counter. Stopping an order already cancelled its dispense; rejecting now does too, with Rejected at verification: … followed by the reason. Covered by
medication-verification.test.ts, walked in FAC-RX-02.
✅ Fixed · The prescriber's chart hid the rejection
Fixed 2026-09-26. The doctor's Prescriptions tab showed a rejected order as On hold · Awaiting dispensing, with no reason. The pharmacist's note reached the doctor only in the notification. The chart now says Rejected by pharmacist with the pharmacist's name and note, and that the order won't be given or dispensed.
✅ Fixed · Pharmacists told "No prescribing integration is on"
Fixed 2026-09-26. The warning read a setting only admins can load, so every pharmacist saw it above prescriptions that had arrived from the chart. It now shows only to someone who can see the setting.
✅ Fixed · Counter supply didn't say whether stock moved
Fixed 2026-09-26. A drug with no stock item (e.g. metronidazole, omeprazole in the demo) was dispensed without touching stock, silently. Supply quantity now reports …stock reduced or …isn't in the stock list, so stock wasn't reduced.
stockTrackedis on the dispense response.
✅ Fixed · Inventory acted on an invisible "selected item"
Fixed 2026-09-26. Purchase orders, reorder policies, recalls, write-offs, transfers and the lot list all acted on whichever item was last chosen in a different card, Amlodipine by default. The purchase-order form had no item field, and the lot list quietly showed only Amlodipine's lots. Each card now has its own Item picker on the same selection, and the lot list and reorder button name the item. The reorder policy took its supplier from the purchase-order card; it now has a Preferred supplier picker. The Low stock filter also shrank every picker on the page; it now filters only the stock list.
✅ Fixed · Purchase orders: drafts couldn't be placed, and goods were received without lot or expiry
Fixed 2026-09-26. A draft purchase order from a reorder suggestion had no way forward: the server's "order" step was never wired to a button. Receiving took its quantity from the new-order form and sent no lot or expiry, so stock arriving by purchase order couldn't be issued in expiry order or recalled by lot. Drafts now have Place order. Receive goods… opens a form on the order itself (quantity defaulting to what's outstanding, lot, expiry date), and the date is sent in the format the server accepts.
✅ Fixed · Inventory copy
Fixed 2026-09-26. Consumables were told Medication provenance incomplete — record … NAFDAC number; only medicines are now. Categories and store types read Medication, Main store instead of
MEDICATION,MAIN STORE. Recent movements, purchase orders and recalls name the item, not only its SKU. Recall and PO statuses are in words. Errors show only in the card that raised them, not in five places. just now ago became just now.
✅ Fixed · The approved stock take hid its review note
Fixed 2026-10-02. Approved stock-take details now show the saved approval note. Walked in FAC-INV-02 as a Facility Admin; the capture shows the approved variance and note.
✅ Fixed · Transfers lost the item's unit in the ledger and queue
Fixed 2026-10-02. Transfer receipt now initializes source and destination balances from the stock item's unit, and the transfer row uses that unit. The regression in
modules.test.tsand the full FAC-INV-02 Chromium walk pass; both balances show tablets after receipt.
Found during the pharmacy batch, outside it (2026-09-26)
✅ Fixed · Inpatient prescribing from an admitted patient's chart
Admission continues to close the outpatient source visit. The chart now confirms the patient's active admission and opens prescribing in that admission's context; after discharge the source visit remains read-only and the composer is unavailable. The server requires a matching active admission for an inpatient order linked to a closed visit. Covered by the admission validation cases in patient-records.test.ts and FAC-IPD-01, walked on a fresh seeded demo as Front Desk, Provider, Nurse, Pharmacist, Facility Admin and Billing Officer. All 11 workflow steps passed in Chromium; the prescription response was asserted to include both source encounter and admission IDs and INPATIENT context. Captures were inspected and retained.
✅ Fixed · Authorization sweep missed outside-record routes
Upload and code-request are separate named token-authenticated routes and are listed as reviewed public routes. Protected patient external-record actions are named explicitly; review is the representative permission behavior. authorization.test.ts passes all 5 tests, and patient-records.test.ts covers upload-token handling.
✅ Fixed · The demo ward had nothing to give at night
Fixed 2026-09-26. Paracetamol and insulin were seeded as started yesterday. Every past dose was signed for, and four-times-daily drugs have nothing due between 22:00 and 06:00, so a demo at night found a round with nothing to record. These orders now start at seed time with the first dose due now. The MAR walks pass at 04:50 as well as at 18:00.
Billing (found writing FAC-BILL-02…04, 2026-09-30)
✅ Fixed · Corporate enrollment left stale choices, patient ID fragments and an error styled invoice confirmation
Fixed 2026-10-02. A successful capitation roster import now refreshes the corporate account's active-coverage picker. The member list shows patient names instead of ID fragments, and a generated monthly invoice uses a green status notice. Walked as Billing Officer in FAC-PAT-02: two imported memberships (one employee, one dependent) were linked to the company account, and the UI confirmed the open ₦2,500 invoice with zero patient responsibility.
- Where: Revenue → Tariffs & payers → Monthly capitation and Corporate accounts.
- What happened: after importing the roster, the account's coverage picker retained the pre-import cache and showed no members. Once those members were linked, the list showed truncated patient UUIDs; after invoicing, the success confirmation used the red error style.
- Fix: invalidate corporate coverage data after roster import, show patient names in the member list, and render invoice confirmation as a success status.
✅ Fixed · Closing a shift showed no reconciliation, and a blank count closed as ₦0
Choosing Close shift reset the card to Open shift with nothing to show for it: no expected cash, no count, no variance. The server computed all three; the page never showed them. An empty Declared cash was sent as ₦0, recording the whole drawer as missing. Now Closed shifts lists who closed each shift and when, expected and declared cash, and Balanced, Short ₦… or Over ₦…. Close shift is disabled until a count is entered. Walked in FAC-BILL-02 step 3.
✅ Fixed · The shift's "collected" figure went stale after each payment
Recording, confirming, adjusting or allocating money didn't refresh the shift card. The cashier counted the drawer against a figure that left out the payments they'd just taken; in the walk it showed ₦28,500 collected when the server expected ₦58,500. Every money-moving action now refreshes the shift.
✅ Fixed · The invoice page took and showed money in kobo
On Open invoice receipt, the payment amount was pre-filled with 500000 for a ₦5,000 bill (one click from taking ₦500,000). Unit price 1500 on a new line meant ₦15, and refunds and split tenders were read the same way. A draft bill also offered a pre-filled Lab test line. All money boxes on the page are now in naira and labelled (₦). The payment defaults to what's actually left to pay, and a new line starts empty.
✅ Fixed · Claim record showed amounts 100× too high and raw statuses
A ₦6,500 claim read Amount: NGN 650,000, its linked invoice the same, with SUBMITTED, DRAFT and PARTIALLY_PAID as codes. The record now shows the patient, amount claimed in naira, what the payer paid with its reference, and a plain-language history.
✅ Fixed · Claims could go to any payer, for the patient's share too
- What happened: the claim picker listed every invoice as INV-000064 — ₦5,000 (self-pay and already-claimed ones included), with no patient. The payer was a free-text box defaulting to NHIA, a name none of the demo payers has. The server accepted any payer for any bill and claimed the whole total, co-pay included. The demo seed did exactly that: it raised claims from self-pay bills, rotating through three payers.
- Now: the picker lists only bills charged to an insurer's coverage that have no live claim, as invoice · patient · payer · amount. The payer is taken from the coverage. The server refuses a claim to any other payer (This invoice is billed to … Claim it from that payer.) and claims only the payer's share. Batch and remittance payers are chosen from the payers in use. The seed now insures most of today's outpatients against verified coverage and claims their bills from the right payer, leaving one for the claims desk. Covered by
modules.test.ts(wrong payer refused, co-pay excluded). Walked in FAC-BILL-03.
✅ Fixed · Remittances were read in kobo, and every claim row shared one amount box
Typing 6500 against a claim recorded a ₦65 remittance, and the batch Received amount the same. The Amount and Reference boxes were shared by every submitted claim, so typing in one filled all of them. Each row now has its own Paid (₦) and Reference, amounts are naira, and the button says Record remittance. Rows show the batch number (Batch CLMB-000001) instead of a raw id. Appeals name the claim number, and 1 claim is singular.
✅ Fixed · Refunds were invisible, left out of the drawer, and reopened a paid bill
- What happened: after Record refund, nothing on the page changed. The payment still read Confirmed in full, with no message. A cash refund wasn't counted against the cashier's shift, so the drawer came up short at close by exactly the refund. Refunding more than was left said Only 3000000 remains refundable (kobo). Refunding an item returned on a paid bill reopened the bill as Partially paid (owing the refund again). The server refused any adjustment on a paid bill, so there was no way to credit the charge first.
- Now: the payment shows ₦500 refunded, a confirmation says what to hand back, and the refund box defaults to what's left on that payment. Cash and POS refunds are attached to the refunding cashier's open shift, as payments are. The limit message is in naira. A paid bill can take a credit note (only), which leaves the patient in credit. The page says so, the credit is refunded, and the bill stays Paid. Adjustment types show as Discount, Credit note and so on, not codes. Covered by
modules.test.ts(refund on the open shift and on the payment; credit then refund stays paid; discount on a paid bill refused). Walked in FAC-BILL-04.
✅ Fixed · Money was converted page by page, and three more places still used kobo
Money is whole kobo in the database and the API, and naira on screen. Each page did its own conversion, and that is where every money bug in this batch came from. The same review found three more:
- The payer benefit cap was labelled Max covered (kobo) and took kobo.
- The bulk tariff CSV took kobo (its example
300000meant ₦3,000). - The claims payer export wrote minor units columns.
The parser also accepted -500 and silently rounded 12.345. Now:
- Every money box is a
MoneyInput: kobo in and out, ₦ shown, at most two decimals, and an on-the-spot Enter an amount in naira for anything else. - The benefit cap and CSV take naira, and the export writes naira to two decimals.
lib/money-guard.test.tsfails the build if a page converts money itself, labels a ₦ amount on anything but aMoneyInput, or prints a money field raw.
Walked in FAC-BILL-02…04, including an invalid amount.
✅ Fixed · Money was naira-only, and Reports showed every figure 100× too high
- Currency: a Currency setting existed, but every money screen hard-coded ₦ and every conversion assumed two decimal places. Now all money goes through one module (
facility-web/lib/money.ts), which takes each currency's decimal places and symbol from the platform's ISO 4217 data: ₦ and GH₵ with 2 places, F CFA with none, the dinar with 3.- New money follows the facility's currency: price boxes, labels, the tariff CSV, and new bills, purchase orders and auto-charges on the server.
- Existing bills keep their own currency, and auto-charges never add to a bill in another currency.
- The settings list covers 15 regional and major currencies, with a warning that entered amounts aren't converted (IMP-07 step 3). The server refuses codes that aren't real ISO currencies. Previously any three letters, e.g. "ABC", were accepted.
- The guard now also fails on a hard-coded ₦ or "NGN".
- Reports: report totals are minor units, but the Reports page formatted them as whole naira, so revenue, cash, payer mix and shift figures all read 100× too high. They now go through the same formatter, and a walk checks the scale (below).

✅ Fixed · Cashier shifts have no currency of their own
Fixed 2026-10-07. A shift records the facility's currency when it opens (migration
0260), and its float, takings and reconciliation are shown in it. Shifts opened before have none and show in the facility's currency. Covered bymodules.test.ts; FAC-BILL-02 passes.
A shift records amounts only. If the currency is changed while a shift is open, its float and takings display in the new currency. Guide says: close open shifts before changing the currency (IMP-07 warns about this).
✅ Fixed · Receivables showed nothing owed
Receivables ageing skipped Draft bills. But bills are never formally issued: they stay Draft until paid, so the report hid nearly every unpaid bill, inpatient running bills included. The demo showed No outstanding receivables with 20 bills unpaid. Unpaid drafts now count; voided and cancelled bills don't. Rows name the patient and who pays (self-pay or the insurer), list the oldest first, and say how many there are beyond the first ten. Covered by modules.test.ts; walked in FAC-BILL-05 step 1.
✅ Fixed · Denials could not be recorded, appeals were pre-written, and an overturned claim could never be paid
- What happened: the remittance control could only record Paid or Part-paid, so Denied, and with it Appeals, was unreachable. The appeal reason was one shared box pre-filled with "The supporting clinical documentation is attached to the encounter.". On the server, a claim could only ever have one remittance. A claim denied, appealed, overturned and resubmitted could therefore never have its payment recorded.
- Now: each claim row has an Outcome (Paid or Denied), and a denial needs the payer's reason. Each denied claim takes its own appeal reason, which can't be blank. Migration
0251rebuildsclaim_remittancesto allow one remittance per submission: the denial stays on record, and the resubmission can be paid. A claim still takes a remittance only while it's Submitted. Appeal and batch statuses read Overturned, Upheld, Reconciled, Short-paid. Covered bymodules.test.ts(deny → appeal → overturn → resubmit → pay; a second remittance on the same submission refused); walked in FAC-BILL-05 step 4.
✅ Fixed · A closed remittance batch stayed attached, invisibly
After Close batch, the batch disappeared from the row's picker but stayed selected underneath, so the next remittance was sent against the closed batch and refused. The refusal appeared in the Draft from invoice card at the top of the page, so nothing seemed to happen. Only an open batch can now be attached, closing a batch clears it, and errors show in the claim queue.
✅ Fixed · Deposits and estimates showed raw patient ids
The open-deposit picker and the top-up warning read 2f9c… has ₦7,000 remaining, the estimates table showed the patient's id, and the statement listed deposits as CASH deposit. They now show the patient's name and the method in words.
Fixed · Transport requires pasted patient and care-context IDs
- Observed 2026-10-04: the fresh-seed FAC-OPS-01 Chromium walk as Transport Coordinator reaches the workstation, but the form has Patient ID, Encounter ID and Admission ID fields instead of a named patient/context selection. The capture fails at the expected patient picker. There is no transport request entry or trip review on the patient chart.
- Follow-up walk: the rebuilt form, full trip lifecycle and chart review passed, but the chart's new-trip link lost the care context because the Transport Coordinator has no clinical visit selection.
- Fixed and walked 2026-10-05: the request form and chart transport card use named visit/admission selection through the minimal transport lookup. FAC-OPS-01 passes in Chromium on a rebuilt production guide stack and fresh Nothing skipped seed; the selected admission survives the chart-to-request handoff. Five screenshots were inspected. The lookup requires transport management and patient-view permissions without granting admission/chart-writing access; wrong-role and patient/facility isolation checks pass. Firefox remains unwalked after a browser-launch failure.
Fixed · Transport cancellation and trip drafts lack usable provenance
- Source trace 2026-10-04: cancellation supplies a generic coordinator reason, hides that reason in the saved trip, and dispatch fields carry across trip selection. Requester/updater names and crew entry are not shown. State updates also lacked a conditional version/status write for competing workstations.
- Fixed and walked 2026-10-05: FAC-OPS-01 verifies the coordinator's full trip lifecycle, retained vehicle/crew and actor names, required and saved cancellation reason, cleared dispatch/cancellation drafts, terminal controls and Front Desk refusal. Conditional version/status writes prevent competing dispatch or state updates from silently overwriting one another. Five server tests and the SDK transport check pass; all five Chromium screenshots were inspected. Firefox layout verification remains pending.
Fixed · CSSD release traceability and theatre handoff
- Observed 2026-10-04: CSSD Technician can register, load, run and release a procedure-linked cycle, but its details hide the saved load note, release note and releasing staff member. FAC-CSSD-01 stops at that missing evidence.
- Found while tracing the source: the load keeps selected sets after creation, release notes can carry across cases, theatre cannot inspect its linked cycles, and manual set updates can return a loaded set to Available before the indicator/release gate.
- Fixed and walked 2026-10-04: FAC-CSSD-01 passed in Chromium on the rebuilt production guide stack, fresh seed Nothing skipped, as Provider → CSSD Technician → Theatre Coordinator, with Front Desk redirected away from CSSD. Its seven inspected screenshots retain load/release notes, actor attribution, failed-load hold, documented reprocessing through a new cycle, and exact source review from theatre. Load/case drafts clear correctly. Manual active-cycle bypass, premature/stale release and conflicting updates are guarded; eight server regressions and the SDK CSSD check pass. Firefox layout verification remains pending.
Fixed · CSSD Refresh retains unavailable selections
- Observed 2026-10-04 (CSSD-001): on a fresh synthetic guide seed, two independently signed-in CSSD workstations select the same set. After one loads it, the other's Refresh disables its checkbox but leaves it checked and counted in the draft. The two-workstation capture fails at the exact unchecked assertion; the server still refuses a conflicting load.
- Intended: remove unavailable selections after refreshed register data arrives, retain other available selections and the method/procedure/notes draft, and let staff review and save the remaining load.
- Fixed and walked 2026-10-04: refreshed register data now removes unavailable selections while retaining available selections and the remaining draft. Both Chromium captures in FAC-CSSD-01 passed on a rebuilt production guide stack and fresh Nothing skipped seed. The two-workstation check retains the method/procedure/notes, saves only the remaining set and opens the exact linked procedure/patient. All ten screenshots were inspected, including recovery screenshots 08–10. CSSD-001 is removed from the open issue tracker; Firefox layout verification remains pending from the earlier launch failure.
Fixed · Laundry patient/admission selection and care-unit handoff
- Observed 2026-10-05: the freshly seeded FAC-OPS-02 Chromium walk as Mrs. Amina Yusuf (Laundry Coordinator) reaches the laundry worklist but fails at named patient/admission selection. The form requires raw IDs; saved details expose the admission ID, and nurses cannot review the linked linen record in the admission.
- Fixed and walked 2026-10-06: bounded operational identity lookup and exact patient/admission nursing read preserve ward/team scope and the coordinator's limited permissions. Fresh-seed Chromium FAC-OPS-02 passed as Laundry Coordinator and Nurse, with Front Desk refused. All five screenshots inspected; six server tests and the SDK check pass. Internal design:
docs/design/laundry-care-handoff.md. Firefox failed before app launch and remains pending.
Fixed · Laundry handling instructions and cancellation provenance
- Source trace 2026-10-05: saved notes, requester/updater and cancellation reason are not displayed. Cancellation supplies a generic reason, the creation draft stays filled after saving, and state changes precheck a version but write unconditionally.
- Fixed and walked 2026-10-06: visible instructions, staff attribution, actual cancellation reasons and cleared drafts are asserted through delivery/cancellation and reload in FAC-OPS-02. The nurse sees the delivered request only in its admission. Conditional version/status writes have competing-update and stale/terminal regression coverage. Six server tests, the SDK check, SDK type check and production build pass; all five Chromium screenshots inspected. Firefox layout verification remains open.
Fixed · Theatre handoff, timestamps, implant validation and chart notes
- Observed 2026-10-04: the Provider's chart order reached the worklist, but the missing-consent checkpoint had no action to capture procedure-linked consent. The walk also exposed implant validation rejecting contract-valid null optional fields.
- Found while tracing the source: timestamps reloaded UTC text as facility time, switching cases retained prior-case fields, the chart omitted procedure notes, note reads collapsed separate procedures into one type chain, and explicit discrepant counts could reuse a previously correct count at completion.
- Fixed: procedure-context consent capture, separate case-panel keys and draft reset, facility-time reload, readable saved states and provenance, contract-aligned implant validation, explicit count refusal, independent procedure-note chains and chart display.
- Evidence: fresh-seed Chromium
capture/theatre.spec.tspassed as Provider, Theatre Coordinator and Billing Officer on 2026-10-04. It asserts the actual consent context, overlapping-resource refusal, checklist refusal, staged saves, timestamp reload, specimen/implant retention, anaesthetic handoff, signed chart note and exactly one procedure charge. Six screenshots were opened and inspected. Firefox failed at macOS sandbox/compositor launch before visiting VERA; its layout pass remains pending. Procedures, patient-forms and admission-notes tests passed (18 tests); facility-date tests passed (23 tests). See FAC-THR-01.
✅ Fixed · There is no "issue bill" step
Fixed 2026-10-07 (decision 3 in
docs/work/decisions-open.md, on Epic's model). A bill is issued when its visit closes or at discharge (a visit that leads to admission stays open until discharge). An issued bill takes no new lines; late charges start a new bill. Receivables age from the issue date. Covered bymodules.test.ts.
Ordinary bills stay Draft until paid. Only estimates converted to a bill and corporate bills become Open. Receivables now count unpaid drafts, but there is no point at which a bill is finalised (e.g. on discharge) and can no longer change.
✅ Fixed · An invoice doesn't know about its claim
Fixed 2026-10-07 (design). A remittance is recorded as a payer payment on the bill. Each bill shows what the payer and the patient owe, have paid and still owe, and receivables split the outstanding amount the same way. On an insured bill the desk takes only the patient's share (single or split tender). The claims queue shows a part-paid or denied claim's Shortfall, settled once, with a reason, as Write off (bill comes down) or Bill patient (patient's share goes up). It waits while an appeal is open, and a settled denial can't then be appealed. Migration
0259records which balance each adjustment changes and the claim it settles. Covered bymodules.test.ts(balances, desk refusal, write-off, bill-the-patient, open appeal, late appeal, front desk refused). Walked in FAC-BILL-03 steps 5–6 as the Billing Officer, Chromium, fresh seed.
A covered bill stays Draft while its claim is submitted and part-paid, and the payer's remittance never reaches the invoice or receivables ageing: the claim and the bill are tracked separately. The shortfall on a part-paid claim (payer paid ₦6,000 of ₦7,000) has no next step (write off, appeal, or transfer to the patient). Guide says: follow part-paid claims up from the claims queue's Partially paid filter.
✅ Fixed · Changing a covered bill resets the patient's co-pay to zero
Fixed 2026-10-01. Each invoice line now stores the patient's share (migration
0255, which backfills existing lines from what their bill already said). Lines added later, by hand or by auto-charge onto a covered draft, use the plan's tariff and benefit. Discounts, waivers and credit notes come off the patient's share first. Billing and the bill page show <payer> pays / Patient pays, and the claims picker shows the amount that will be claimed. The demo seed now gives three plans a consultation co-pay. Covered bymodules.test.tsandinvoice-line-patient-share.test.ts. Walked in FAC-BILL-03 step 0 as the Billing Officer.
A covered invoice's co-pay is worked out per line when the bill is created. Any later change (adding a line, an adjustment, a refund) recalculates the bill with patientResponsibility = 0 for covered bills, and the per-line split isn't stored to recompute it. After that the patient owes nothing at the desk, and a new claim would be for the whole total. Found by reading recalculateInvoice, not in the walks. Fix: store each line's patient share and recompute from it.
Found during the billing batch, outside it (2026-09-30)
- Tenant CORS takes one origin.
VERABASE_TENANT_SERVER_CORS_ORIGIN="http://localhost:3210,http://localhost:3200"is sent back as-is, and browsers reject it, so sign-in silently does nothing. Use a single origin (the capture runs usehttp://localhost:3210). blood-bank.test.tsis date-bound. Its unit expires2026-09-30T08:00Z, so from that moment the transfusion step fails with BLOOD_COMPATIBILITY_EXPIRED. Fix: dates relative to now.- ✅ Fixed (demo data) · Nurses couldn't see today's walk-ins (2026-09-30 → 10-01). Staff without a ward assignment get facility-wide access, unless they have ever been on a treatment team; then they see only their teams' current patients. The Vera Chat seed had made Sr. Chioma Eze, the general nurse the walkthroughs use, the treatment-team nurse, so she lost every outpatient and the ED. The access rules are unchanged. The seed now gives the team role to a new demo nurse, Nurse Grace Okon (
nurse.okon@demo-clinic.test), who also opens the demo team conversation, and the chat walk and FAC-CHAT pages use her. Re-walked: every facility journey and all six chat walks pass on a fresh seed, apart from the known FAC-IPD-01 step 4 below. Answered 2026-10-08 (decision 2, on Epic's model): no. Treatment-team membership no longer narrows access; only a ward or unit assignment does. The seed now assigns Nurse Grace Okon to the wards of her team's admissions, which keeps her census for the break-the-glass walk. - Shared demo stack. Other sessions use the default demo (tenant-server
:8788,.data/demo) and restart it with their own settings mid-walk. The guide now runs on its own stack (capture/guide-stack.sh::8798+:3210,.data/guide), anddemo-reset.shdeletes only the database its registry names. - Unrelated failures in the tenant-server suite from the parallel external-records work: the authorization sweep can't resolve the new route at
app.ts:1169, andcross-facility-patient-merge.test.tsregisters a patient that comes back without an id. Not touched.
The lab bench (found writing FAC-LAB-02, 2026-10-01)
✅ Fixed · A rejected specimen reached nobody, and couldn't be rejected on arrival
What happened:
- Reject specimen only appeared after the specimen had been received, although most rejections happen on arrival. The server allows both.
- Its reason box had no label.
- A rejection wrote an audit row and told no one: the requesting doctor's chart listed it, collapsed under Other lab orders, as just Rejected.
- On a closed visit the chart told a doctor You don't have permission to order labs. Ask an admin for
lab.createOrder, which was false and showed a raw permission key.
Now:
- Rejection is offered at receipt and after, with a labelled reason.
- The requester gets a Specimen rejected notification with the test, patient and reason, linking to the chart's labs.
- The chart shows the reason and opens the list when it holds a rejection.
- A closed visit says This visit is closed. Open today's visit to order a test.
Covered by
lab-results.test.ts; walked in FAC-LAB-02 steps 1–2.
✅ Fixed · Acting on an order could silently switch the bench to another patient's order
The selected order was taken from the filtered queue. When an action moved it out of the filter (e.g. Awaiting result → At reference lab), the page fell back to the first open order, a different patient's, with no sign. A technician could then record a result on the wrong order. The order being worked now stays selected and is loaded directly. The reference-lab card also kept the previous order's Tests sent when switching orders; it now starts fresh for each order. Walked in FAC-LAB-02 step 3.
✅ Fixed · Raw lab codes on screen
The queue filters read REFERENCE_LAB and REJECTED, the send-out badge SENT/RETURNED, and the analyser help RESULT_PENDING/HL7_V2. The queue also said just now ago. All now read in words.
✅ Fixed · Inpatient seeded lab orders have no visit link
Fixed 2026-10-07 (decision 5). A lab order placed while the patient is admitted records the admission, and the chart lists it under Lab orders during admission instead of Other lab orders. Covered by
modules.test.ts.
Seeded outpatient lab orders now link to the matching open visit; an outpatient panel is omitted if that visit has not started. Ward lab orders remain patient-level because admission closes the source visit, and the lab order has no admission link, so they appear under Other lab orders on the chart. Decide whether ward orders should link to an admission episode. FAC-LAB-02 now rejects a sample ordered during an antenatal visit and verifies that the rejection stays on that visit; all five steps pass on a fresh seed.
Break the glass (found writing FAC-BG-01/02, 2026-10-01)
✅ Fixed · High · Any break-glass header bypassed the patient boundary
- What happened:
- The ward/unit/team check was skipped whenever a request carried an
x-verabase-break-glass-grantheader, whatever its value. A scoped clinician could add a made-up header and read or write any patient's records with her normal permissions. - A valid grant also switched off every permission check on any read naming the patient (
?patientId=), including admin and audit routes. - Writes under a valid grant passed for staff who can write in their own scope, so "read-only" wasn't true for nurses or doctors.
- The ward/unit/team check was skipped whenever a request carried an
- Now: only a valid, authorised grant relaxes the boundary, and only on reads of its patient. It adds chart-reading permissions, not all permissions. A write under a grant is refused with Emergency access is read-only…. Covered by
break-glass.test.ts: a made-up header gets 404; a write under a grant gets 403BREAK_GLASS_READ_ONLY; an admin route with?patientId=gets 403.
✅ Fixed · Break-the-glass could not work in a browser, and scoped clinicians couldn't reach it
What happened:
- The grant header wasn't in the server's CORS allow-list, so a browser blocked every request that carried it: the chart showed Failed to fetch.
- The request form only appeared for staff with no patient permission at all. A nurse or doctor outside their coverage hit dead ends: search said No patients match this search, and the chart showed Patient was not found in your assigned unit twice above an empty chart shell offering Start today's visit.
- The grant lived in one page's memory, so leaving that page lost it.
Now:
- The header is allowed.
- A refusal outside your coverage has its own code (
PATIENT_OUT_OF_SCOPE), and the chart and profile show a Break the glass panel instead of an empty shell. - An empty search offers break-the-glass by patient number (wristband or card).
- The grant is kept for its hour in the browser tab and sent with every request about that patient, with an Emergency access · read-only · until … banner.
- The review card names the patient, shows when the chart was opened, needs a note before Approve/Reject, and the audit trail refreshes after a decision.
Walked in FAC-BG-01 (as Nurse Grace Okon) and FAC-BG-02 (as the facility admin).
✅ Fixed · Chart insights are unavailable under emergency access
Fixed 2026-10-08 (decision 1, owner-approved). Insights are part of the chart under a grant: the browser sends the grant with the insights request and the insights service accepts the granted patient. Covered by
break-glass.test.ts; FAC-BG-01 checks the request isn't refused (the guide stack runs no intelligence service, so the summary itself shows unavailable for everyone there).
On a chart opened by break-the-glass, Chart insights says Insights are unavailable. Try refreshing. The assistant's summary isn't among the reads a grant allows. Allergies, medicines, results and history all show. Decide whether the summary belongs in emergency access.
✅ Fixed · The emergency chart still shows its recording controls
Fixed 2026-10-08 (decision 1, owner-approved). As in Epic, a clinician records under a grant within their own role, for the granted patient only; each write is audited as BREAK_GLASS_<method> and listed in the review. Covered by
break-glass.test.ts(write allowed and audited, prescribing still refused for a nurse, another patient refused). Walked in FAC-BG-01/02 as Nurse Grace Okon and the Facility Admin.
Under emergency access every recording control (vitals, problems, notes, orders) is still on screen. Each is refused with the read-only message, but they should be hidden. Product question: should break-the-glass allow recording in a true emergency (for example a resuscitation note), and with what review?
Found during gap-log batch 1 (2026-10-01)
- ✅ Fixed · The payment box kept the first bill's amount. On Billing, the payment amount was set once, from the first bill selected, and stayed when the cashier picked another bill. Adaeze's ₦9,000 bill offered ₦5,000. It now follows the selected bill and resets after each payment. Walked in FAC-OPD-01 step 7.
- ✅ Fixed · The claims picker showed the bill total, not the amount claimed. With co-pays, INV · patient · payer · ₦8,000 drafted a ₦7,500 claim. The picker now shows the payer's share. Walked in FAC-BILL-03 step 1.
- ✅ Fixed 2026-10-07 · Auto-charges start a self-pay bill even for an insured patient. (decision 4) A new auto-charged bill now carries the patient's verified coverage.
- ✅ Fixed 2026-10-07 · Mock-mode billing keeps the old co-pay rule. Offline desk adjustments now come off an insured patient's share first, as on the server.
- ✅ Fixed (walks) · The billing walks named a patient the seed no longer gives that bill (2026-10-07). FAC-BILL-02…05 expected Sani Nwachukwu's ₦50,500 bill. The seed rework that matches visit reasons to patients' ages gave him two smaller bills, so the walks failed in every browser. FAC-BILL-02 now picks the first unpaid self-pay bill of ₦25,000 or more with a medicine on it, avoiding patients other journeys use. FAC-BILL-04 credits and refunds that same bill, and FAC-BILL-05 avoids it. Passed 7/7 in Chromium and in Google Chrome on fresh seeds.
- ✅ Fixed 2026-10-07 · A discount didn't show on the bill. The billing page now updates the bill from the adjustment's response instead of waiting for the list re-fetch.
- ✅ Fixed 2026-10-08 · An undeliverable email showed "unexpected error" to an outside clinician. When the email service can't be reached, Email me a code now answers 503 We couldn't send the code just now. Try again in a few minutes, or ask the person who invited you. Covered by
vera-chat-live.test.ts; the delivered-code path is walked in FAC-CHAT-09 (the failure itself was seen in the browser before the fix, not re-walked after). - ✅ Fixed (walk) 2026-10-08 · Chat walks drifted and the call walk lacked a relay. FAC-CHAT-07 searched for a hard-coded patient the seed no longer puts on the escalation; it now uses the patient FAC-CHAT-01 finds. FAC-CHAT-05 sent before the composer held the whole message; it now waits. FAC-CHAT-08 needs a TURN relay; the capture README now runs one locally. All ten chat walks pass on a fresh seed.
- ✅ Fixed (walk) 2026-10-07 · FAC-OPD-02 named an arrival the seed had already checked in. The walk expected Aisha Umar among the not-yet-arrived appointments; the seed's patient matching now books her into an earlier, checked-in slot. The walk picks the first appointment still waiting to be checked in.
- ✅ Fixed 2026-10-07 · FAC-OPD-01 step 3 failed once. The walk now waits for the chart page (up to 20s) before checking its text; a slow first chart load had outrun the 5s check.
- Product decisions for break-the-glass, treatment teams and bill issue are written up in
docs/decisions-open.md. The claim-to-bill design is indocs/design/payer-payments-to-bills.md. Problems in the parallel external-records and Vera Chat work are handed over indocs/handover-parallel-work.md.
Reports (found writing FAC-REP-01, 2026-10-08)
✅ Fixed · The report builder showed raw kobo, codes and UTC times
- Observed 2026-10-08: FAC-REP-01 in Chromium, Facility Admin, fresh Nothing skipped seed. An invoice report had columns Total (kobo), Paid (kobo), Balance (kobo) with values like
500000for a ₦5,000 bill. Statuses and visit types were codes (DRAFT,EMERGENCY) and times were UTC (2026-10-08T07:12:15.954Z). The CSV was the same. - Fixed 2026-10-08: the report catalogue says what each column holds (money, time, date, code) and the report carries its currency. The page shows money in that currency, codes in plain words and times on the facility's clock. The CSV has decimal amounts with the currency in the header (
Total (NGN)), facility-local times and the same words. Covered bycustom-report.test.tsandcustom-reports.test.ts; walked in FAC-REP-01.
✅ Fixed · The builder's status filter was a box for typing codes
- Observed 2026-10-08: Status (optional) was free text with the placeholder
ACTIVE/OPEN. Anything else matched nothing, without saying so. - Fixed 2026-10-08: Filter by status is a list of the dataset's statuses in plain words. The server refuses any other status (422
VALIDATION_FAILED).
✅ Fixed · The builder used dates from another card
- Observed 2026-10-08: the builder had no dates of its own. It silently used the Period summary start and end dates further down the page.
- Fixed 2026-10-08: it has its own From and To, with a note saying which date counts (invoices created, patients registered, …).
✅ Fixed · The builder cut reports at 200 rows without saying so
- Observed 2026-10-08: the page asked for 200 rows at most, yet said "Download CSV for the full result".
- Fixed 2026-10-08: a report holds up to 500 rows (the server's limit), and the page says when a report reaches it.
✅ Fixed · Finance detail and summary cards showed codes and developer wording
- Observed 2026-10-08: Revenue by department listed
CONSULTATION,BED,LAB; Collections by method listedCASH,TRANSFER; cashier shifts showedOPEN, under a 1 shifts badge. Unsynced records read "Pending local sync events (device-side on Flutter)". - Fixed 2026-10-08: plain words throughout (Consultation, POS terminal, Open, 1 shift); the sync card reads "Records waiting to sync from devices".
✅ Fixed · Imaging and Radiology were separate revenue departments
- Observed 2026-10-08: Revenue by department had both Radiology (4 invoices) and Imaging (2 invoices). The built-in catalogue filed the chest X-ray under
RADIOLOGYwhile the demo catalogue usedIMAGING. The catalogue form offered neither, and showed its categories as codes. - Fixed 2026-10-08: one list of service areas (
serviceItemCategories), with Imaging, shown in plain words on the catalogue form and list. Migration0263moves existingRADIOLOGYitems toIMAGING. Walked in Chrome: Reports shows one Imaging row (6 invoices), and IMP-04 checks the category list. Covered byservice-category-imaging.test.ts.
✅ Fixed · Daily summary small wording
- Observed 2026-10-08: Patients today read "Registered on 2026-10-08", and the period summary, finance badge, KPI footer and IDSR card showed ISO date ranges. Invoice revenue said "Open + paid invoice totals" but counts every bill opened that day, drafts included.
- Fixed 2026-10-08: dates in the facility's format (Registered on 08/10/2026, 02/10/2026 → 08/10/2026). The revenue card reads "On bills opened this day, issued or not", which matches decision 3 (a bill gathers charges until its visit closes). Walked in Chrome in FAC-REP-01.
✅ Fixed · Tariff windows showed ISO dates
- Observed 2026-10-08 in Chrome, IMP-04: Active price rules read "2026-09-01 → open".
- Fixed 2026-10-08: "01/09/2026 → no end date", in the facility's date format; IMP-04 asserts it (Chrome).
Accreditation (found writing FAC-GOV-04, 2026-10-08)
All fixed 2026-10-08 and walked in Chrome as the Accreditation Coordinator on a fresh Nothing skipped seed (FAC-GOV-04). Server rules covered by accreditation.test.ts, landings by home-route.test.ts.
✅ Fixed · Review actions acted on the first record, not the one you were looking at
- Found 2026-10-08 (source, before the walk): Review evidence, Accept evidence and Request update sat under the register and always changed
evidence[0]. A pack with two records could never have its second one reviewed. - Fixed: each record carries its own actions, by state (Send for review, Accept, Request update, Resubmit, Reopen, Archive).
✅ Fixed · A pack could be submitted empty, or with unaccepted evidence
- Found 2026-10-08: the server let a pack go for review with no evidence and be submitted with records still in draft. Evidence in a submitted pack could still be reviewed and changed. The page's Close button on a pack ready for review called a transition the server refuses.
- Fixed:
PACK_EMPTYandPACK_NOT_READY(409) refuse those moves, andPACK_LOCKEDrefuses review changes after submission. The page disables the buttons and says how many records are accepted. Back to draft replaces the broken Close.
✅ Fixed · "Request update" sent a fixed note, and the owner had no way to update
- Found 2026-10-08: the reviewer couldn't say what was wrong ("Update the evidence before submission." every time), and nothing on the page let the owner change the record.
- Fixed: the reviewer's own note is required (
REVIEW_NOTE_REQUIRED). The owner corrects the description and source reference on the record and resubmits it. The record shows who reviewed it, when, and the note. Sending a record for review no longer counts as a review.
✅ Fixed · Creating a second pack left the first one selected
- Observed 2026-10-08 in Chrome: after Create pack, the register stayed on the previous pack, so new evidence would have gone into the wrong pack.
- Fixed: the new pack is selected and stays selected.
✅ Fixed · Codes, ISO dates and forms that kept their text
- Found 2026-10-08:
READY FOR REVIEW,TRAINING RECORD,FACILITY LOCAL, review dates as2026-11-30; the pack and evidence forms kept their text after saving, inviting a duplicate. - Fixed: plain words (framework names spelled out), dates in the facility's format, overdue reviews marked, and both forms clear after saving.
✅ Fixed · Operations roles landed on Staff leave
- Observed 2026-10-08 in Chrome: the Accreditation Coordinator signed in to Staff leave. From the source, so did the Laundry and Transport coordinators, and the CSSD Technician landed on Procedures. Their workstations weren't in the sign-in landing list, so the first page they could open (leave, which everyone may request) won.
- Fixed: each lands on their workstation (Accreditation evidence, Laundry, Transport, Sterile processing). The CSSD, laundry and transport walks still pass in Chrome.
- Fixed 2026-10-08 (Mortuary): the register moved from Reports to its own Operations → Mortuary page, and the Mortuary Officer lands there. Walked in Chrome in FAC-OPS-04.
✅ Fixed · Patient search kept a stale match after intake
- Observed 2026-10-08 in Chrome (FAC-OPS-04, also in the 2026-10-07 screenshot): after Receive body cleared the form, the patient search box was empty but the previous match, Sani Nwachukwu, stayed listed and clickable, inviting a second intake. The shared patient picker drew results from the last search, not from the text in the box.
- Fixed: the picker clears its results and its "no patient matches" note when a patient is chosen, and only lists matches for the text in the box. Asserted in
capture/mortuary.spec.ts.
✅ Fixed · One coordinator can add, accept and submit evidence alone
- No second person was required to accept a record. Fixed 2026-10-08 (owner decision 6): whoever added a record can't accept it, and the record shows who added it and who accepted it. Tenant-server test; FAC-GOV-04 re-walked in Chrome with the coordinator adding and the facility admin accepting.
Control plane
✅ Fixed · A smaller plan switched off modules without a word
- Observed 2026-10-08 walking CP-06 as the Platform Admin: moving a live clinic from Professional to Starter saved at once. Clinical Notes, Lab and Inpatient still read Enabled in Module provisioning, but the hospital's server refuses any module outside the licence (
MODULE_NOT_LICENSED), so its doctors would have lost the chart with no warning to anyone. - Fixed: the platform API refuses a plan change that turns off granted modules (
PLAN_DROPS_GRANTED_MODULES, naming them) unless it is confirmed, and audits which modules were turned off. The licence card names them before saving and needs Turn these off and change the plan ticked. A granted module the plan doesn't cover now says Granted, but not in the … plan: the hospital can't use it. Three API tests; walked in CP-06.
✅ Fixed · Archive in one click; suspend with no explanation
- Observed 2026-10-08: Archive (final) and Suspend acted on the first click, nothing said what either does to the hospital, a suspended hospital offered Activate, and a refused change threw an error page.
- Fixed: the Lifecycle panel says what each state means, Suspend… explains the effect and asks for confirmation, Archive… needs the slug typed out, a suspended hospital offers Reactivate, and refusals show as a message. Walked in CP-06.
✅ Fixed · Licence card: raw codes, US-order dates, hidden "saved"
- Observed 2026-10-08:
GRANTED,ACTIVE, the plan key, module keys, and 10/8/2026 (read as 10 August in Nigeria); after the first plan was assigned, the success message sat inside the collapsed Change plan section, so nothing confirmed the save. - Fixed: plain words (Licensed, Active, module names), dates written 8 Oct 2026, 09:12, and Plan saved. shown on the card.
✅ Fixed · Integrations card: raw API errors, no way to add a key later, raw codes
- Observed 2026-10-08 walking CP-07 as the Platform Admin: a duplicate name showed
platform-api /api/v1/platform/tenants/…/connections -> 409: {"code":"INTEGRATION_SLUG_EXISTS",…}; a connection saved without its key stayed Inactive with no way to add the key or turn it on or off (the API allowed it, the page didn't); a key shorter than four characters created the connection and then failed, leaving a half-made one; rows showedpaystack · card-payments,creds …9f2aandINACTIVE; failures showed raw codes; provider notes said (CP-3 billing). - Fixed: plain messages for every refusal (an old one clears as soon as the form is edited), a short key refused before anything is saved, Add key / Replace key and Turn on / Turn off on each connection, Paystack · key ending 9f2a and On / Off, plain failure reasons and Send again, and plain provider notes. The API now refuses to turn a connection on without a key (
INTEGRATION_CREDENTIALS_REQUIRED; two tests). After a save, the provider box and its note no longer disagree. Walked in CP-07 in Chromium and Chrome.
Partly fixed · The hospital doesn't use the connections set here
Found writing CP-07 (from the code): the hospital's server never read platform-admin's connections, so turning one on changed nothing for the hospital.
Fixed for SMS and WhatsApp 2026-10-08 (owner decision 7). Active Twilio and WhatsApp Business connections travel in the hospital's runtime record. Reminders, patient messages and portal codes go out from the hospital's own number or sender ID. WhatsApp uses the hospital's approved templates; sign-in codes use its authentication template. When a connection is off, its channel goes back to the shared relay. Control-plane and hospital-server tests; CP-07 checks the runtime record in Chromium and Chrome.
Still open:
- Analysers post straight to the hospital.
- The DHIS2 return isn't sent.
- Payments wait for payment webhooks.
- The platform's own integration jobs still only acknowledge.
P2.
✅ Fixed · Patient numbers in local form couldn't be sent through a provider
- Found 2026-10-08 building decision 7 (from the code): a message's recipient is the patient's number as staff typed it (0803 123 4567), and portal numbers are bare digits. Twilio and WhatsApp need international form, so most Nigerian patients' messages would have been refused.
- Fixed: numbers go out in international form, using the country of the hospital's facility (+2348031234567). A local number with no country set fails with a reason that says what to record. Tests in
hospital-messaging.test.ts.
✅ Fixed · A new hospital was never backed up automatically
- Found 2026-10-08 writing CP-08 (from the code): the worker's daily backup only covered hospitals with a backup policy, and a policy was created only when someone pressed Run backup now. A hospital nobody backed up by hand was never backed up. Hospitals with no database or archived ones were scheduled too.
- Fixed: every live or suspended hospital with a database is backed up daily unless its policy is switched off, with no duplicate while one is queued; a backup of a hospital with no database is refused (
BACKUP_NO_DATABASE). Three tests.
✅ Fixed · Backups card: no reason for a failure, no restore-test results, stale until reload
- Observed 2026-10-08 walking CP-08: a failed backup read Failed · Database with no reason; restore test results weren't shown anywhere; the card stayed on Running until a reload; a hospital with no database was offered Run backup now (which then failed); the restore test was offered only on the latest backup, so one failed backup hid it; raw
turso-pitr://…keys and developer text. - Fixed: recent backups and restore tests with time and outcome, failures in words that say who acts, the card follows running jobs, the restore test uses the newest backup that worked, and plain explanatory text. Walked in CP-08 in Chromium and Chrome.
Open · A successful backup and restore test have not been walked
- Local walks have no hosted database service (Turso), so only the failure path was walked. A walk of the success path needs a Turso test organisation and its token on a test control plane, which is the owner's to set up. P2.
✅ Fixed · Clinical intelligence: one vague refusal, raw source types, a free-text priority list
- Observed 2026-10-08 walking CP-11 as the Platform Admin: turning intelligence on with anything missing gave the raw
platform-api … 400: {"code":"INVALID_INTELLIGENCE_CONFIGURATION","message":"Check model profiles, jurisdiction, collection grants, and data-sharing policy."}without saying which; source priority was a text box ofhospital_protocol,keys where a typo failed the save; the heading said revision not configured. - Fixed: the API names the failing check (no model, no region, no collection, a bad country code, a missing or switched-off model, a cloud model under private-only sharing; one new test). The page shows that message alone, plain type names, and an ordered priority list with arrows. The heading says saved version N.
✅ Fixed · Knowledge library and access: raw errors, raw states, no live updates
- Observed 2026-10-08: document states read
queuedandready, and the import message said to refresh the page by hand. Knowledge access on a hospital that isn't live offered Grant access and then failed with Check your permissions and the tenant status. The expiry showed as2027-04-05. - Fixed: Waiting to process, Ready for review and Approved, in use. The list follows processing by itself. Knowledge access waits for a live hospital and says so, refusals give their reason, the date picker allows only the next year, and the expiry reads 5 Apr 2027.
✅ Fixed · The knowledge service and its worker crashed when started together
- Observed 2026-10-08 setting up CP-11: starting the Intel API and ingestion worker at the same time (as its Docker Compose file does) made both create the schema at once, and one crashed (
duplicate key value violates unique constraint "pg_type_typname_nsp_index"). - Fixed: schema setup holds a database lock, so the second process waits. An integration test runs three at once; it fails without the lock.
Open · Model profiles are edited as raw JSON
- Seen in CP-11: Platform model profiles is a JSON text box. A mistake is now refused with the reason, but there is no form. It is platform-wide and rarely changed, and wasn't walked. P3.
Open · Staff of a suspended hospital see "No active tenant"
- From the code, not a walk (the local control plane can't provision a hospital database): once suspended, the hospital's server answers every request with No active tenant matches this request. Staff aren't told their hospital's access is paused or whom to call. P3.
Open · An archived tenant's page still offers changes
- Seen in CP-06: after archiving, Module provisioning, the licence form and the profile form are still active on the page. Nothing reaches a hospital (an archived tenant is never served), but the page should be read-only. P3.
✅ Fixed · Activation discloses when the tenant database is missing
Fixed 2026-10-02. An Active tenant without a provisioned database now displays a page-level warning. Walked in CP-02 as the Platform Admin against the local capture database; the warning and resulting Active page were captured.
- Where: platform-admin → Tenant → Lifecycle → Activate;
platform-tenants.service.ts(activation). - What happened: when provisioning wasn't configured, the tenant became Active with no database or Facility admin credentials panel, and nothing on the page said so. Support then showed the tenant as Critical / Runtime offline.
- Fix: the tenant page now says its database is missing and provisioning is not configured.
✅ Fixed · Unlicensed module grant says "missing permission"
Fixed 2026-09-24. Modules outside the licence (
effectiveModules) show "Not included in the <plan> plan — change the plan or add a manual entitlement first.", with Grant module disabled. AMODULE_NOT_ENTITLEDresponse that still gets through now reads as a licence problem, not a permission one. Walked in CP-02 step 4.
- Where: Module provisioning → Grant module (
module-actions.ts:88maps every 403 to this text). - What happens: granting Pharmacy under the Standard plan returns
MODULE_NOT_ENTITLED("not entitled under the current license. Upgrade plan or add a manual entitlement"). The UI says "Your account is missing the required module permission." The platform super-admin is told they lack a permission they have. - Fix: show the API's error message, or map
MODULE_NOT_ENTITLEDexplicitly. Better still, disable Grant module for modules outside the plan, the way dependency blocks already are.
Fixed, not walked · Organisation form fields have no labels
Fixed 2026-10-07, not yet walked. Each field on Add organization has an accessible name (Organization name, Organization slug, Legal name, Tax ID). The operator walk (CP-02) needs the local control plane and wasn't run.
Add organization uses placeholders only (organization-form.tsx), so screen readers announce unnamed text boxes.
Go-live (found writing IMP-01 … IMP-08, 2026-09-24)
✅ Fixed · Interaction rules fired but were never shown
Fixed 2026-09-24. The server checked facility interaction rules and stored matches on the order, but no screen displayed them: prescribers and pharmacists never saw a warning, and a contraindicated pair failed with no reason field. Now the full composer pre-checks while typing and shows severity, mechanism and "What to do"; a contraindicated pair needs a reason; orders carry an interaction badge; the quick composer handles the contraindicated refusal and reports non-blocking warnings after placing; and the pharmacist's verification list shows the interaction and the prescriber's reason. Walked in IMP-05.
✅ Fixed · A refusal blocked the next, different prescription
Fixed 2026-09-24. Refusals returned by the server (allergy, duplicate therapy, dose safety, organ function, interaction) were cleared only after a successful prescription. After one refusal, changing to another drug left Prescribe disabled until the page was reloaded. They now clear whenever drug, dose, unit, route or frequency change. Walked in IMP-05 (paracetamol ceiling, then simvastatin).
✅ Fixed · Service catalogue and tariffs priced in kobo
Fixed 2026-09-24. Catalogue unit price, payer price and capitation monthly rate are entered in naira. The bulk tariff CSV intentionally stays in kobo, and says so on screen. Walked in IMP-04.
✅ Fixed · MFA enrolment is only reachable by Facility Admins
Fixed 2026-09-24. Every user now has Account & security in the account menu (
/account), with Set up MFA, recovery codes, Disable MFA and their active sessions. The admin settings page shares the same card and adds the tenant MFA policy. Walked as a doctor in FAC-ACC-01: enrol, sign in with a code, disable.
✅ Fixed · No self-service password change or reset for hospital staff
Fixed 2026-09-24. Account & security → Password changes your own password. It needs the current password, keeps this workstation signed in and signs out every other session. Forgot password? on the sign-in page emails a one-time link that expires in 15 minutes and is retired when a newer one is requested. Using it signs out every session. The request answers the same for unknown emails and is rate-limited. The email goes out through the tenant server's email webhook (
VERABASE_EMAIL_WEBHOOK_URL, link baseVERABASE_FACILITY_WEB_URL). Walked in FAC-ACC-01 and IMP-01.
✅ Fixed · MFA policy could lock out a privileged account that hadn't enrolled
Fixed 2026-09-25. With the tenant MFA policy on, a privileged account without MFA was refused at sign-in ("Ask a facility administrator to complete enrollment"), which was impossible: enrolment needs a session. This hit anyone who turned MFA off to change phones, and meant a new doctor could only be hired in three steps. Now a correct password returns a single-use, 10-minute enrolment ticket, and the sign-in page sets MFA up in place. No session opens until the first code is confirmed. New privileged accounts can be created while the policy is on. Giving a privileged role to an existing account still needs MFA first, because its open sessions would otherwise continue without it. Covered by
mfa-sign-in-enrollment.test.tsand walked as a new doctor incapture/mfa-policy.spec.ts(IMP-02 step 5).
✅ Fixed · Custom role builder shows raw permission keys
Fixed 2026-10-02. The role editor now presents all 151 permissions with plain-language task names and descriptions, grouped by work area, with search. Administrators can start from a role preset and review the permissions it adds; the Facility Admin preset warns that it grants every permission. Updated
capture/golive.spec.tsselects the Housekeeping preset, removes housekeeping management, adds four Ward Clerk permissions, checks the eight-permission total, and saves the role. The IMP-02 Chromium capture passed on a fresh demo seed as Facility Administrator; the permission editor screenshot was inspected and retained inassets/imp-02/04-custom-role.png.role-permission-catalog.test.tspasses all 3 tests and covers the product permission catalog.
✅ Fixed · Scanner attestation was offered during dose recording
Scanner registration, activation, attestation and token replacement now live under Devices → Managed devices, gated by device.registerDevice. The MAR no longer offers Attest device; nurses enter the current facility token only to start a short-lived round session. Device registration and status controls are also hidden without the admin permission. medication-barcode.test.ts confirms a nurse receives 403 when attempting the attestation API directly. Walked on a fresh demo in FAC-IPD-01: Facility Admin attested the ward scanner, then Nurse Chioma started a session and recorded the dose; the complete 12-step workflow passed in Chromium. The inspected screenshot is retained in the journey.
✅ Fixed · Demo visit reasons and orders did not fit their patients
Fixed 2026-10-02. The morning and future appointment selector now matches pregnancy to an adult woman, paediatric complaints to a child, and adult follow-ups to adults; admitted patients are kept out of clinic appointments. Aisha Garba, Funke Obi and Zara Yusuf stay attached to the cough, child dyspepsia and diabetes cases used in FAC-RX-02 and FAC-RAD-01, and are not assigned to inpatient admissions or seed allergies that conflict with their intended orders. Outpatient medication and imaging orders now use the encounter matching their indication. The pulmonary oedema report is already released as a critical result, while the STAT tension-pneumothorax order remains unreported for the radiographer workflow. The diabetic-foot inpatient order uses clindamycin for the seeded patient with a beta-lactam allergy. A fresh
guide-stack.shseed ends Nothing skipped. Walked as Pharmacist and Radiographer/Provider on the fresh seed:pharmacy.spec.tsandradiology.spec.tspass (7 tests); the screenshots were inspected and retained underassets/fac-rx-02/andassets/fac-rad-01/.
✅ Fixed · Licence expiry field has no label
Fixed 2026-10-07. The council, registration number and expiry date are labelled. IMP-02 now fills them by label.
The date input next to Registration number on the Staff page is unlabelled for screen readers.
Environment notes (not product gaps)
- The local
cloud-platform/.envpointsDATABASE_URLat a hosted Neon database. Capture runs overrideDATABASE_URLandDIRECT_DATABASE_URLto local Postgres; seecapture/README.md. - Device-side behaviour after a lock or wipe request is not in the capture walks (there is no device app in the local environment).
- Firefox: Playwright's Firefox, Puppeteer with the installed Firefox 156, and a direct
firefox -headlessall fail to start from the automation environment ("Could not find profile folder"). The walks ran in Chromium. Run the Firefox pass from a normal terminal:BROWSER=firefox npx playwright test.
✅ Fixed · Mortuary save refusals are invisible
- Observed 2026-10-06: fresh-seed Chromium FAC-OPS-04 as Mr. Samuel Okafor (Mortuary Officer) reached the register and attempted intake for Sani Nwachukwu without a death record. The required refusal message was absent; the capture failed at the visible-alert assertion.
- Fixed 2026-10-07: visible refusals, cleared completed forms, saved authorization details and preserved intake/disposition notes passed in all three Chrome captures. Six screenshots inspected on a fresh Nothing skipped seed. See FAC-OPS-04; MORT-001 is resolved.
✅ Fixed · Provider cannot file the mortuary prerequisite death record
- Observed 2026-10-07: all three FAC-OPS-04 Chrome scenarios reached the named patient's profile as Provider Dr. Tunde Balogun, but showed “No mortality record has been filed” without the filing form. Fresh isolated seed completed with Nothing skipped; production webpack build passed. The prerequisite-refusal screenshot was retained, but the complete journeys failed.
- Cause: both death-record creation/review API routes and the profile card use patient-registration permission. The Provider preset lacks that permission, while Front Desk has it.
- Fixed 2026-10-07: dedicated clinical permission granted to Provider and Facility Admin, with aligned UI/API. Five mortality/custody tests pass, including Provider filing/review and refusal of Front Desk, Nurse and Mortuary Officer. All three Chrome role walks pass with six inspected screenshots; see FAC-OPS-04.
✅ Fixed · Specialty observations hide saved plans and measurements
Fixed 2026-10-07. Saved rows show every recorded field for their chart type, the form submits only the selected type's values and clears after saving, and the observed time is read on the facility's clock. FAC-SPEC-01 passes in Chromium with the browser in America/Chicago: Provider records all four types, Nurse reviews them. Covered by
specialty-observation-fields.test.ts.
- Observed 2026-10-07: FAC-SPEC-01 in Chrome, Provider Dr. Tunde Balogun, Aisha Garba's seeded outpatient chart. The saved mental-health row showed mood, risk and author but omitted the submitted safety follow-up. The exact saved-plan assertion failed. The fresh seed ended Nothing skipped; both existing server tests passed.
- Source findings: saved rows also omit dental plans, eye pressures/findings/plans and physiotherapy interventions/goals. The form retains completed values and submits every chart type's fields, allowing previous-type values into a later observation.
- Additional source finding: the observation input uses facility wall time but submission parsed it in the browser timezone. Submission now uses the existing facility timezone converter; the capture explicitly runs in America/Chicago and checks the selected facility time survives saving and handoff.
- Done: all recorded fields shown, type-scoped submission, completed drafts cleared; Provider → Nurse review walked with screenshots inspected.