API + Agent layer, scheduling overhaul, and clinical depth#2
Merged
Conversation
Visits the public GitHub repo in a clean browser context and verifies first-time visitors see the expected repo landing state — README images load, CI badge is present, no page errors. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
apps/web had only Playwright e2e; pure logic (mappers, auth helpers) had no unit coverage. Adds vitest with a node environment and a manual @/ alias, a 'test' script, the turbo 'test' task, and a CI step so unit tests run on every push/PR. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The apiKeys table existed but was never connected to any auth path. Adds: - An indexed key_prefix column so a raw key can be narrowed to a small candidate set before a constant-time bcrypt compare (bcrypt hashes can't be looked up directly). - lib/api-auth.ts: parse Bearer/X-API-Key, verify, enforce scopes and a per-key rate limit (reusing lib/rate-limit.ts), return a tenant context. - An admin api-keys tRPC router (create/list/revoke) that returns the raw key exactly once and stores only the bcrypt hash. - Unit tests for key generation, header extraction, and scope checks. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
A versioned public REST surface for third-party integrators, built as thin Next.js route handlers that query Drizzle directly and pass rows through a pure, isolated mapper layer. Response shapes are owned by an explicit Zod contract and frozen independently of the internal schema. Endpoints: GET clients, GET clients/:id, GET patients (with ?client_id), GET patients/:id, POST appointments. The appointment write fires the previously never-called appointment.created webhook. Every query is scoped by practiceId + isNull(deletedAt); access is gated by scoped API keys. Mappers normalize internal vocabulary to integrator-friendly shapes (species canine->dog, sex enum split into sex + neutered). Covered by mapper unit tests and contract tests asserting output satisfies the schema. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Adds docs/api/README.md (auth, scopes, rate limits, error format, endpoints), a README section surfacing the API, and a CONTRIBUTING guide for adding compatibility endpoints/targets plus the unit + e2e testing workflow. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Closes a clinical-depth gap vs leading PIMS (ezyVet/Provet have dosing calculators; OpenVPM had none). Adds a pure calculator engine + a curated starter formulary (8 common drugs with species-specific reference ranges), exposed via a dosing tRPC router (formulary + calculate). - Weight-based mg range, optional injectable volume from concentration, practical tablet-split suggestions, and a hard max-single-dose cap. - Guard rails: rejects non-positive/implausible weight, unknown drugs, and refuses to extrapolate across species. Every result carries a verify-before- prescribing disclaimer and drug-level warnings (e.g. carprofen not for cats). - 14 unit tests covering math, capping, tablet logic, and guard rails. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Clinical-depth gap: OpenVPM tracked weight only. Leading PIMS (Cornerstone, Instinct) capture full vitals per visit. Adds a vital_signs table (temp, HR, RR, weight, body condition score, pain score, mucous membrane, CRT, notes) with patient/appointment/recorder links, plus a vitals router (listByPatient + record) gated to clinical roles with sane physiologic input bounds. Note: additive schema change — run pnpm db:push on deploy. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The foundation for an agent-operated practice. An AI agent can now read and act on practice data through a typed tool registry, always scoped to one practice: - lib/agent/tools.ts: 6 tools (find_client, get_patient_summary, list_appointments, book_appointment, list_overdue_vaccinations, calculate_drug_dose) each with a JSON schema for the model + a Zod schema for runtime validation. Write tools are flagged. - lib/agent/runner.ts: Claude tool-use loop (Anthropic SDK) with prompt caching on the system prompt + tool defs, write-gating (allowWrites, default false), max-iteration guard, and graceful degradation when ANTHROPIC_API_KEY is absent. - agent tRPC router (status + run), gated to admin/vet. - 8 tests: registry integrity, read/write flagging, the pure dosing tool, and validation guards. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Reframes the pitch around the validated insight that practices won't rip-and-replace their PIMS. New homepage section positions OpenVPM as the open data layer you connect to your current system — a live, exportable copy of your own data you control — plus an OpenVPM Agent callout (operates the practice over the open API, every write gated, scoped to your data). Adds an /updates changelog page to push product notes out (API v1, the Agent, dosing + vitals, the new direction), wired into nav, footer, and sitemap. Email capture via the existing waitlist is linked from the updates CTA. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Dashboard page (admin/vet) to run the OpenVPM Agent: instruction box with suggested prompts, an opt-in 'allow writes' toggle, the agent's answer, and a collapsible tool-call trace so staff can see exactly what it did. Shows a clear setup notice when ANTHROPIC_API_KEY isn't configured. Added to the sidebar. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
README: OpenVPM Agent subsection (tools, scoping, write-gating, BYO key). .env.example: ANTHROPIC_API_KEY + optional AGENT_MODEL with a note that the app works fully without them. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Audit gap: stock quantities were never decremented when products were sold, so inventory drifted from reality. Adds a pure computeStockDeductions helper (aggregates product lines, ignores services and unlinked lines) and wires it into createInvoice: real (non-estimate) invoices now decrement each product's stock, clamped at zero so it never goes negative. 5 unit tests on the helper. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Closes the #1 audit gap (no self-service booking). Clients can now request an appointment from the portal: pick a pet, choose an appointment type, and a preferred date/time. The request creates a real 'scheduled' appointment on the practice calendar (flagged '[Portal request]') for staff to confirm, mirrors into the communications inbox, and fires the appointment.created webhook. - pure buildRequestedSlot helper (date/time validation, duration-based end, no-past-dates) with 5 unit tests. - portal.getAppointmentTypes query + upgraded portal.requestAppointment (type-aware, ownership-checked, rate-limited 5/hour per portal link). - New /portal/[token]/book page + 'Request appointment' button on the portal appointments page. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Ties the API-compat layer and the agent together: an external automation (or an agent-led ops layer) can drive the practice through one natural-language endpoint. POST /api/v1/agent, authenticated by an API key with the new agent:run scope, runs the agent scoped to that key's practice. allow_writes (default false) gates write tools; returns the answer plus a full tool-call trace, or 503 when no ANTHROPIC_API_KEY is configured. Documented in docs/api with the new scope and endpoint. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Clinical-depth gap: notes existed but there was no structured treatment planning. Adds treatment_plans (linked to a patient and optionally a problem) + treatment_plan_items (ordered interventions with status), a treatmentPlans router (listByPatient with a progress summary, create-with-items, updateStatus, updateItemStatus — all practice-scoped and clinical-role gated), and a pure summarizePlanProgress helper (skipped items excluded from the denominator) with 5 unit tests. Note: additive schema — run pnpm db:push on deploy. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Lowers switching cost — practices can migrate from their current PIMS using the common CSV export format instead of hand-building JSON. Adds a pure, dependency-free CSV parser (quoted fields, embedded commas/newlines, escaped quotes, CRLF) and typed row mappers with normalized header matching (First Name == first_name), returning valid records plus per-row errors for a partial import. Wired into data.importClientsCsv / importPatientsCsv (admin), reusing the existing insert + email->client linking. 8 unit tests. Pairs with the existing JSON export in data.ts. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Pushes the agent-led thesis further by letting the agent use the clinical features just shipped. Adds list_treatment_plans (read, with progress summary) and record_vital_signs (write, gated; recordedBy left null since the agent isn't a user row). Registry test updated for the new write tool. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Recurring-revenue parity feature (ranked #3 in the audit). Adds wellness_plans (plan definitions: price + monthly/annual interval) and wellness_enrollments (client/patient, status, nextBillingDate). Pure date math (computeNextBillingDate with month-end + leap clamping; enrollmentsDueOn) with 9 unit tests. wellness router: listPlans, createPlan, enroll, listDue (active enrollments due on/before a date), markBilled (advances the next billing date), cancel — practice-scoped and role-gated. Charge capture is deferred until a payment processor is wired; markBilled advances the schedule. Note: additive schema — run pnpm db:push on deploy. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Makes the vitals feature usable by clinicians where they work. Adds a Vitals tab to the patient detail page with a compact record form (temp, HR, RR, weight, BCS, pain, notes) and a history table, mirroring the existing self-contained-tab pattern (trpc.vitals.listByPatient + record, with cache invalidation and toasts). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Addresses Adam's note that the calendar wasn't up to snuff. Two real defects: back-to-back appointments (one ends exactly when the next starts) were flagged as conflicts because the overlap check used non-strict comparisons, and only doctor conflicts were checked — a room could be double-booked freely. Adds a pure, tested conflict engine (lib/scheduling/conflicts.ts): strict interval overlap, doctor AND room detection, cancelled/no-show excluded, reschedule-aware (excludeId), with a clear conflict message. Wired into appointments.create (now also validates end > start) and createRecurring (skips occurrences that clash on doctor or room). 13 unit tests. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The calendar had no way to move an appointment's time, doctor, or room — only status changes. Adds appointments.reschedule: validates end > start, preserves the existing doctor/room when not explicitly changed, and runs the conflict engine excluding the appointment being moved so a drag-to-reschedule can't false-positive against itself. Unblocks calendar drag-and-drop in the UI. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Adds /updates entries for the scheduling correctness overhaul (strict overlap, room double-booking, reschedule) and the clinical wave (vitals, treatment plans, wellness plans, online booking) so the changelog reflects what shipped. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
A real calendar suggests free times instead of making staff eyeball gaps. Adds a pure findOpenSlots engine (working window + slot length + busy intervals -> free slots; supports a finer step than the slot, never runs past day end, back-to-back aware) with 5 unit tests, exposed as appointments.availableSlots (by date, duration, optional doctor/room, working hours). Reusable by the portal booking flow and the agent. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Connects the availability engine to a real surface: the portal booking form now fetches open times for the chosen date (public portal.availableSlots query, practice-wide busy intervals) and shows them as tappable chips that fill the time field — instead of asking clients to guess a time blind. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Closes the booking loop for the agent: it can now find free times on a date (optionally per doctor/room) via the availability engine, then book one with book_appointment. Read-only. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
A platform push toward an on-par-with-the-leaders PIMS plus the foundation of an agent-led product. 24 commits, 119 unit tests, full monorepo type-check + build green. Grounded in our user conversations: practices won't rip-and-replace their PIMS, so the wedge is "a second PIMS you own" + an API/agent layer, and the calendar needed to be trustworthy (per advisor feedback).
What's included
Platform / API
/api/v1) for clients, patients, appointments, with scoped, per-practice API keys (bcrypt + indexed prefix lookup, scopes, per-key rate limiting). Response shapes frozen independently of the DB via a mapper layer./agentconsole) and over the API (POST /api/v1/agent,agent:runscope). Tools: find clients/patients, patient summary, list/find-open-slots, book appointment, overdue vaccinations, dosing, list treatment plans, record vitals.Scheduling / calendar (addresses "calendar not up to snuff")
appointments.reschedule(move time/doctor/room) and an open-slot availability engine +availableSlotsquery; portal booking suggests open times.Clinical depth
Revenue / data
Marketing site
/updateschangelog; email capture wired.Testing
Migration notes
pnpm db:pushon deploy:api_keys.keyPrefix,vital_signs,treatment_plans(+ items),wellness_plans,wellness_enrollments.ANTHROPIC_API_KEY(+AGENT_MODEL) to enable the agent; everything else works without it.Not included (needs credentials / approval)
Stripe (payments + wellness capture), Twilio/Resend (reminder sending), IDEXX/Antech (labs), Digitail live-mirror (requires Digitail partner approval + DPA).
Verification
pnpm test(119 passing),pnpm type-check,pnpm buildall green. Live DB round-trips pendingpnpm db:pushagainst a real Postgres.🤖 Generated with Claude Code