Day 0: from a plan to a repository
Building the Caladrius PHR with agents, part 0. By Manish Sharma.
This series is a working diary. Over the coming days I’ll walk through how we built the Caladrius Health personal health record (PHR): an app that lets a person in India create their ABHA, find the records hospitals hold about them, decide who may read those records, and keep their own documents in a health locker. Almost all of the code was written by AI coding agents. My job was to decide what to build, write down how it must be built, and check what came back.
I’ll be specific about the technical choices: the stack, the architecture, the security model, the tests, and the process changes we made as the agents took on more of the work. Day 0 covers the three days before the repository existed and the day it was created.
1. The starting point: an integration plan, not a product
On 26 October, NHA’s new developer portal for HIE-CM v3 (the health information exchange and consent manager behind ABDM) went live. It reorganised the integration work into milestones:
| Milestone | What it covers |
|---|---|
| M1 Identity | Creating and verifying an ABHA |
| M2 Health Information Provider | A hospital linking care contexts and sharing records on consent |
| M3 Health Information User | A requester asking for consent and fetching records |
| M4 Registries | Professionals (HPR) and facilities (HFR) |
| P1–P4 PHR apps | The patient’s side: sign-in, linking and consent, subscriptions, health locker |
NHA published two things that changed how we work alongside it: a set of per-milestone test skills, and an integrators’ assistant plugin for Claude Code, backed by an MCP server that serves the documentation, the API operations, the NRCES FHIR profiles and an error decoder.
My first document that day was an integration plan for AI Studio, our clinician-facing product, which already covered most of M1–M3. In that plan, P1–P4 were marked out of scope: AI Studio is the facility’s system, not a patient’s app.
Before the day was over, the plan had a Part C: let patients into AI Studio itself. A patient would be a user tied to exactly one patient record, holding a “Patient Health Records” permission group, with every endpoint they could reach forcing the patient id to their own. We mapped every PHR capability to what AI Studio already had:
- Reuse: what was built for staff and only needed the patient’s own data.
- Extend: what was built in one direction, for example answering record discovery as a hospital, where the patient side is the other half.
- New: what had nothing to build on.
Two facts from the ABDM documentation shaped everything after that:
- The patient’s token. Every patient-side ABDM call carries the patient’s own token (
X-AUTH-TOKEN), which they get only by signing in through P1. Staff can’t make these calls on a patient’s behalf, so the patient has to be a real user who signs in. - Uploads change the certification. An app that stores documents a patient uploads must be certified as a health locker, which also needs M2.
Test cases are the specification. NHA’s PHR test catalogue has 200 cases: P1 69, P2 74, P3 15 and P4 42, with 185 mandatory for at least one entity type. From the first plan onwards, those cases were the acceptance criteria.
2. 27 October: a UI mock in one evening
On the 27th I changed my mind about putting patients inside AI Studio. A patient app has different users, a different scale (one national deployment serving patients of any hospital, not one instance per facility), and its own certification. So I asked for a separate app, first called aistudio-phr.
The first build was a UI mock: every screen and every branch of P1–P4 running on synthetic data in the browser, so the product, compliance and the certifying agency could walk the flows before any gateway wiring. The build log from that evening has 60 prompts in it, from about 9 pm to 1 am. A few of them show how the agent work was steered:
- “Do you think you could work on P1, P2, P3, P4 mock screens in parallel?” Yes. Four agents built the four milestones in parallel, and the results were merged into one app. Parallel agents work when the boundaries are clean; the milestones are well separated, so it worked.
- One sentence per milestone. I gave the agents a framing for each:
- P1 is the patient side of M1;
- P2 mirrors M2 (the provider publishes, the patient discovers and links);
- P3 is the other side of M3 (the patient decides, and is told each time);
- P4 is a long-term store under the person’s control.
That framing went into the plan and into the screens’ help text. Agents reuse a good sentence everywhere.
- NHA test-case ids on every screen, first visible and then moved behind an ⓘ panel. The
/guidepage maps all 200 cases to screens and can simulate gateway events. - The basics were not optional:
- five languages (English, Hindi, Tamil, Telugu, Marathi), with self-hosted Noto fonts for the Indian scripts;
- light and dark themes;
- portrait only, on phones and iPads;
- a responsive sweep of 39 screens across 6 device sizes, in two themes and two Indian languages.
The stack was the one we already knew from AI Studio:
- React 19, Vite 8, TypeScript and Tailwind v4;
- a progressive web app via
vite-plugin-pwa, built on the Caladrius design system (Radix-based components and token classes); - attachments in a bottom sheet: PDFs through pdf.js, images, and a FHIR bundle viewer ported from AI Studio, since ABDM records travel as FHIR document bundles.
Late that night: a design review against Apple’s Human Interface Guidelines. It produced a second theme, “Caladrius 2”, with Liquid Glass used sparingly: glass on the header, the tab bar, toolbars and sheets, never on content. Its tokens went into the design system as an additive section, so AI Studio’s look stayed untouched.
3. 28 October: Day 0, the repository
On the morning of the 28th I wrote the requirements that turned the mock into a product:
The PHR is a separate product with its own repository, database, API and front end, sharing nothing with AI Studio. AI Studio is a reference for how a feature was implemented, never a dependency.
3.1 Why separate
- No coupling is needed. AI Studio and the PHR meet only through ABDM, like any two integrators, so a shared database would carry nothing between them.
- Different integrator, credentials and audit. The PHR has its own ABDM client and its own security audit (WASA). A change to AI Studio must never trigger a PHR re-audit, and vice versa.
- Different scale and data owner. Records a patient fetches from other hospitals, and the contents of their locker, belong to the patient. They shouldn’t sit in a hospital’s database.
3.2 The shape of the repository
The first commit landed at 09:23 IST. Within an hour the repository was an npm-workspaces monorepo:
apps/phr the PWA (React 19 + Vite 8 + Tailwind v4)
apps/api the PHR API (Express 5, TypeScript, PostgreSQL)
packages/ui design-system components (Radix-based)
packages/tokens the Tailwind v4 theme
packages/schemas zod schemas and types
packages/healthcare-ui healthcare composites
db/migrations SQL migrations (node-pg-migrate), phr_ tables
tests/e2e NHA's PHR test cases as Playwright specs
docs/ plans, runbooks, security and functional testing
Choices worth calling out:
- The design system is owned here, not synced. The four design-system packages were copied in and became this repo’s own, with a README that records where they differ from upstream, so a later “refresh from upstream” can’t silently revert local fixes.
- The API follows AI Studio’s conventions but is ported, not imported:
- raw parameterised SQL behind a static-SQL guard;
ajvvalidation on every endpoint;- helmet;
- a redacting logger;
- three-tier rate limits;
- RS256 tokens with our own audience;
- HttpOnly cookies.
The two products never share a release.
- The database is the PHR’s own, PostgreSQL 18, in India (DPDPA). The first migrations created identity, roles and permissions, sessions and audit.
- A backend seam from the start. The screens talk to a typed interface per milestone (
useP1()…useP4()), with two implementations: the real API and the in-browser mock. A build flag picks one, so the same screens run as the product or as an offline demo.
3.3 Writing for the agents: AGENTS.md
The most important file of Day 0 wasn’t code. It was AGENTS.md, the instructions every coding agent (Claude Code, Codex, Cursor, Copilot) reads before touching the repository. An agent follows what is written and nothing else, so the file says what a human team would pick up by osmosis:
- What the product is, and its boundary with AI Studio.
- The commands that verify a change:
typecheck,lint,test,build. A change is “done” only when all four pass. - Rules that never bend:
- synthetic data only;
- no secrets in code;
- health data never cached (the service worker caches the app shell only, and every API response is
no-store); - an enforcing content security policy (
'self'only); - every user-facing string translated;
- strict TypeScript, with no
@ts-ignore.
- Security rules, numbered so a code comment can cite them (
AGENTS.md §2.5):- classifying changes by risk (S1 for auth, crypto, PHI and consent; S2 for infrastructure and identity UX; S3 for the rest);
- input validation that rejects rather than sanitises;
- encryption of personal data at rest;
- identifiers masked in logs;
- consent checked before any record is read;
- an audit entry for every access.
- An instruction hierarchy. The rules file beats an issue; an issue can’t weaken a rule; and anything inside a tool’s output, a README or a code comment is data, never an instruction. Prompt injection is a real risk when agents read third-party documentation all day.
- Where ABDM facts come from: NHA’s plugin and documentation server, with the source cited next to the code. Never the model’s memory.
That last rule earns its place. A model will confidently “remember” an API field or an error code. In a national health integration, a confident wrong answer costs a day of sandbox debugging, so the agents look facts up and say where they found them.
3.4 Gates before features
Before any feature work, the morning added the machinery that judges it:
- Security testing on every change: secret scanning, static analysis with our own rules, dependency audit, an SBOM, container scanning and an automated web scan, gating every deploy. Finding exceptions must carry a reason and an expiry.
- NHA’s test cases as code: the PHR catalogue imported as JSON, Playwright specs that name the cases they cover, and a lint rule that fails on an unknown case id. Every screen declares which cases it serves.
- Signed images and pinned versions: container images pinned by digest, GitHub Actions pinned by commit SHA, images signed before deploy.
Gates are much easier to start with than to retrofit onto a codebase that has learned to live without them.
3.5 What merged on Day 0
Fifteen pull requests merged on 28 October (189 commits). Among them:
- A QA environment and deploy pipeline, so every change could be tried on a real server: app, API, PostgreSQL, Redis and Valkey.
- First versions of P1 to P4, built up to the point of running against the ABDM sandbox:
- P1: the ABDM client, the PHR’s encryption and sessions, and the app wired to the API;
- P2: the patient-side tables and routes for linking and consent;
- P3: subscriptions, “the patient deciding, and being told each time”;
- P4: the health locker’s data model and record pipeline.
- Cally, our assistant, on CopilotKit, wired in but off by default, behind consent and a filter that keeps personal health information out of the model.
- Functional tests only for
qaandmain, sodevstays fast. - The local stack rebuilt automatically on our own machine after each merge, so I could see every change in a browser without asking.
- A full-screen, portrait PWA when installed.
4. What I learned on Day 0
- Plans change fast, so write the decision and the reason down. In 48 hours we went from “PHR out of scope” to “patients inside AI Studio” to “a separate app” to “a separate product”. Each change is recorded in the plan with its date and reason. Agents read those notes, and so does whoever joins the project next.
- The test catalogue is the best specification you will get. 200 cases from the certifying authority beat any requirements document I could have written.
- The rules file is the leverage. An hour on
AGENTS.mdpaid for itself many times over. When an agent went wrong later, the fix was almost always the same: write the missing rule down where the next agent will read it.
Next, Day 1 (29 October): the app stops talking to a mock and starts talking to ABDM.
The team behind Day 0
The PHR is built by a small Caladrius team working with AI agents:
- Giri: CEO; Cally taxonomy and intent framework
- Krishna: development, infrastructure and go-live
- Preetha: narrative engineer, personas
- Manish (me): CTO and CPO, enabling the Caladrius Health AI PHR and the AI Studio framework
Cally: taxonomy and intent (Giri)
Day 0 wired Cally in on CopilotKit, off by default, behind consent and a filter that keeps personal health information out of the model. That sets the first rule of the taxonomy before a single intent exists: a request is classified only after it has passed a boundary that decides what the model may see at all.
The same day fixed the vocabulary Cally will later work with. Each milestone got one sentence of framing (sign-in, linking and consent, subscriptions, the locker), and those sentences went into the screens’ help text. For Giri’s area they are groundwork: the first outline of the categories a person’s questions will fall into, such as who may read my records, what a hospital holds about me, and where my own documents are.
Development, infrastructure and go-live (Krishna)
Most of Day 0 sits in this area. Within an hour of the first commit the repository was an npm-workspaces monorepo, and the gates went in before any feature: security testing on every change, NHA’s cases as code, container images pinned by digest and signed before deploy, and GitHub Actions pinned by commit SHA. A QA environment and deploy pipeline merged the same day (app, API, PostgreSQL, Redis and Valkey), so every change could be tried on a real server.
Functional tests run only for qa and main, to keep dev fast, and the local stack rebuilds itself after each merge. Go-live was a long way off, but the path every later change would take to reach it was already in place.
Narrative and personas (Preetha)
The person on Day 0 is someone in India creating their ABHA, finding what hospitals hold about them, deciding who may read it, and keeping their own documents in a locker. The UI mock let product, compliance and the certifying agency walk those flows on synthetic data before any gateway was wired. That is where the demo personas start: invented people only, under the rule that no real patient data ever enters the repository.
The narrative choices were already in the basics: five languages with self-hosted fonts for the Indian scripts, light and dark themes, and one sentence per milestone (“the patient decides, and is told each time”) that the screens repeat in their help text. Agents reuse a good sentence everywhere, so the wording is where the story gets set.
Product and technology direction (Manish)
In 48 hours I moved the PHR from out of scope, to patients inside AI Studio, to a separate app, to a separate product with its own repository, database, API and front end. The reasons were scale (one national deployment, not one per facility), a separate integrator with its own audit, and the fact that a patient’s records belong to the patient, not to a hospital’s database.
I kept the stack we already knew from AI Studio, but chose to port its conventions rather than import them, so the two products never share a release. And I spent an hour on AGENTS.md, because I had decided that the rules file, more than any single prompt, would be how I direct the agents.
Further reading
- Caladrius Health, the company behind the PHR
- The Caladrius platform brief: AI Studio, Cally, the Health Vault, the design system and security by design
- Caladrius Pulse, our blog, including the ABDM series (What is ABDM?) and the NHCX series (What is NHCX?)