Canonical source: FEATURES.md. This page is generated by docs/scripts/sync-handbook.mjs. Edit the source file in the repo; changes appear here on the next build.
Basis Platform — Feature Status
AGENTS: Read this before touching any feature. Update the relevant row when you complete work. Format:
Status | Last changed | What changed | Known issuesFor bugs: Find the feature row → read Key Files → check Known Issues before writing any code.
Three-file system:
FEATURES.md(this file) — what exists now, current state, known issuesROADMAP.md— what to build next, YAML task definitions with test criteriaCLAUDE.md— how to work here, rules, patterns, gotchas
Versioning
| App | Current Version | Last Release | Distribution |
|---|---|---|---|
| Basis Hybrid (iOS) | See basishybrid/pubspec.yaml | App Store via Transporter | TestFlight → App Store Review |
| Basis Hybrid (Android) | See basishybrid/pubspec.yaml | Google Play Console | Internal testing → Production |
| Basis Flow Web | N/A (SPA) | Firebase Hosting (continuous) | firebase deploy --only hosting:platform |
| Basis Web | N/A (SPA) | Netlify (auto-deploy on push) | Git push to branch |
| Cloud Functions | N/A (per-function) | firebase deploy --only functions:name --force | Per-function deployment |
How to Update This File
After completing work on any feature, update its row:
- Status:
🟢 Stable/🟡 Active Dev/🔴 Known Issues/⚪ Untouched - Last Changed: Date + brief description (not a full changelog — one line)
- Known Issues: Add new ones, remove fixed ones
Platform Apps
| App | Status | Last Changed | Key Path | Notes |
|---|---|---|---|---|
| Basis Flow Web (staff) | 🟡 Active Dev | 2026-03-15 archive/delete workflow, proration fix, multi-location availability | hybrid/basisflow-web/ | Main staff platform. Deploy: firebase deploy --only hosting:platform |
| Basis Web (client portal) | 🟡 Active Dev | 2026-03-04 bank transfer processing status | hybrid/basisweb/ | Deploys to Netlify (NOT Firebase). Auto-deploy on push |
| Basis Hybrid (mobile) | 🟢 Stable | Active maintenance | hybrid/basishybrid/ | Flutter. DO NOT flutter clean |
| Basis Flow (legacy Flutter) | ⚪ Deprecated | — | hybrid/basisflow/ | ⛔ DO NOT MODIFY |
Backend Infrastructure
| Feature | Status | Last Changed | Key Files | Known Issues |
|---|---|---|---|---|
| Firebase Functions | 🟢 Stable | — | basis-functions/functions/src/ | Always use GB_1 memory. Never deploy all at once |
| Firestore Rules | 🟢 Stable | — | basis-functions/firestore.rules | Each settings/{name} needs its OWN explicit rule — no wildcard |
| Storage Rules | 🟢 Stable | — | basis-functions/storage.rules | hasPermission() checks BOTH implicit_permissions and permissions arrays |
| Firebase Auth | 🟢 Stable | — | Auth via Firebase Console | Custom domains must be added to Auth → Authorized Domains |
| Activity Log | 🟢 Stable | 2026-03 | functions_activity_log.py | LLM-friendly audit logging. Categories: CATALOG, CLINICAL, PROTOCOL, SETTINGS, SCHEDULING, USERS, COMMUNICATION, BILLING, DATA, SECURITY. Stored at clinicsv2/{clinic}/activity_log/{id}. Used by copilot and cron functions for audit trail. |
| Scheduled Crons | 🟢 Stable | 2026-03-04 status transitions | functions_clinic.py | process_waitlist_notifications (every 15 min). process_client_status_transitions (every 15 min, GB_1): auto-reactivates paused clients, auto-deactivates scheduled clients. |
| CI/CD Pipeline | 🟢 Stable | 2026-03-06 | .github/workflows/, .github/scripts/ | Autonomous dev pipeline: nightly orchestrator (2 AM UTC) → triage agent (Haiku) → build agent (Sonnet) → QA agent (Haiku). Slack notifications for all events. See docs/claude/operations.md. |
| Health Check | 🟢 Stable | 2026-03 | health-check.yml, functions_clinic.py → health_check | Hourly health check. Tests Firestore, Auth, GCS. Alerts on failure. |
| Clinical Audit Trail | 🟢 Stable | 2026-03-08 | functions_clinical_audit.py | create_clinical_audit() helper for logging clinical data changes. Stores at clinicsv2/{clinic}/clinical_audits/{id} with before/after snapshots. Used by superbill auto-generation and agent execute tools. |
| Superbill Auto-Generation | 🟢 Stable | 2026-03-08 | functions_superbill.py | Firestore trigger on notes with billing codes → auto-creates draft superbill. Separates ICD-10/CPT. superbill_service callable for status management (draft → submitted → accepted/rejected). 2026-07-26: structured lineItems + fees + provider/patient blocks + totalFee (#448); server-side branded patient-facing PDF via superbill_service get_superbill_pdf (#450); auto-generate on/off via settings/billing.superbillsEnabled toggle (#452). 2026-08: on ISSUE the superbill PDF is saved to the client's documents (users/{uid}/files/superbill_{id}.pdf) via _persist_superbill_document so cash-pay / OON clients can self-retrieve it (#800). |
| Insurance Billing / RCM (Stedi) | 🟡 Active Dev | 2026-08-21 batch: billing workspace + Ready-to-bill queue (#795), Review & submit scrubber + AI denial-risk (#797), secondary/COB claims (#784), corrected/voided claims (#785), policy-level prior-auth/referral #s + per-procedure modifiers (#806), coverage badge (T4), unmatched-remittance reconcile (#739), auto-eligibility nightly + at-booking (#786), per-client billing method + AoB (#658) — all deployed | functions_stedi.py, stedi_client.py, stedi_claims.py, stedi_scrubber.py, billing/page.tsx, InsuranceClaimsSection.tsx, CoverageBadge.tsx, ClientDetailPage.tsx | Full pipeline: eligibility 270/271, 837P submit + resubmit + secondary(COB) + corrected(freq 7)/void(freq 8), 999/277CA/835 lifecycle webhook, ERA reconcile by PCN + unmatched-remittance capture, /billing workspace (Ready-to-bill · Claims · Transactions). Billing method (insurance/courtesy/time_of_service) + AoB drive claim flags. Prior-auth/referral are POLICY-level (no per-claim UI). Demo clinic axuk-khwf-prkr sandboxed (test key eligibility; claims → Stedi Test Payer). ERA enrollment E2E needs a real practice NPI (fake demo NPI is ERA-enrolled elsewhere — exclusive per NPI). Collections metering (#14) next. 2026-08-21 — #802 deployed (stedi_denial_intelligence, stedi_generate_appeal, stedi_outcome_rollup): Denials & underpayments worklist — denied/underpaid claims decode their CARC/RARC into a plain-language reason + a deterministic next action; AI drafts a payer-ready appeal letter grounded in the clinic's own denial-outcome history and NEVER auto-submits (biller reviews → sends → "Mark appealed" logs the result to the denial-outcome ledger, which sharpens future drafts). 2026-07-26: multi-specialty provider-scope enforcement + auto therapy modifiers (GP/GO/GN) + AT/25/59-XP warnings, rendering provider sourced from the billed appointment (#608); future-date submit guard + editable resubmit date + provider/linked refs in the claim drawer (#616). See docs/claude/insurance-rcm.md · docs/features/billing-insurance.md. |
Core Features
Scheduling
| Sub-feature | Status | Last Changed | Key Files | Known Issues |
|---|---|---|---|---|
| Booking | 🟢 Stable | — | functions_clinic.py → book_appointment | Membership booking window constraints applied; test with Admin not System Admin |
| Google Calendar 2-way sync (#558) | 🟢 Stable | 2026-07-21 book/edit/reschedule/cancel propagate to the clinic's Google Calendar | functions_google_meet.py (calendar_link_fields/patch/delete), functions_clinic.py | Reschedule PATCHes in place (Meet link preserved); cancel deletes (sendUpdates=all); waitlist swap deletes the original attendee's event so they stop getting updates. Verified live (tools/verify_558_calendar_sync.py). Forward-only: events booked before deploy have no linkage. |
| Reschedule across services (#561) | 🟢 Stable | 2026-07-21 reschedule modal gains Service selector + staff-eligibility guard | EventDetailsDrawer.tsx, functions_clinic.py → update_event_details | Backend rejects a service the resulting staff don't offer (fail-open when service has no coach roster); audit txn carries old/new service name. NOT retroactive — old wrong-service events need re-reschedule/Edit or a targeted backfill. |
| Availability | 🟢 Stable | 2026-03-15 multi-location coach availability | functions_clinic.py → find_availability_intervals, StaffDetailPage.tsx | Availability blocks use ISO weekday (1=Mon, 7=Sun) — NOT 0-indexed. SlotModal supports multi-location selection (checkboxes) — creates one block per location with deduplication. |
| Group Services | 🟢 Stable | 2026-02-02 materialization fix | functions_clinic.py → materialize_group_series | Timezone shift fixed; use UTC midnight for date creation |
| Waitlist | 🟢 Stable | 2026-01-25 locationId filter removed | functions_clinic.py → join/leave/assign_from_waitlist | get_waitlist returns list[dict] (not model) to preserve entryId |
| Cancellation | 🟢 Stable | — | functions_clinic.py → cancel_appointment | Late cancellation detected from membership cancellationPolicy |
| Rescheduling | 🟢 Stable | — | tools/test-reschedule.js | Use test tool to verify duration preserved after move |
| Unavailability | 🟢 Stable | — | clinic_service → create_unavailability | createdByUid stored on events; transactions logged |
| Link appointment to a program (#257) | 🟢 Stable | 2026-08-17 associate an appointment with one of the client's active programs from its details drawer | EventDetailsDrawer.tsx (editProgramId, "Link to program"), functions_clinic.py (edit-appointment program_id, empty = unlink), model_event.py (Event.protocolId) | See docs/features/link-appointment-to-program.md. Clearing the selection unlinks. |
| Open-Gym / QR Check-In (#721) | 🟢 Stable | 2026-08-17 members scan a per-location QR to check in with no appointment; appear in dashboard "In Session" | functions_clinic.py → open_gym_checkin (attended drop-in event, idempotent, optional credit), model_clinic.py (Location.openGymServiceId, Service.dropInCheckinEnabled/dropInDeductsCredit), dashboard/page.tsx ("In Session"), basishybrid modal_checkin_scanner.dart | Check-in events carry source='open_gym_checkin'. See docs/features/qr-check-in.md. |
Next: Google Calendar two-way sync (Phase 3). Recurring appointment management improvements.
Labs
| Sub-feature | Status | Last Changed | Key Files | Known Issues |
|---|---|---|---|---|
| Lab Upload & Analysis | 🔴 Known Issues | — | functions_ai_documents.py, document_processor.py | ImportLabModal / UploadDocumentModal in Basis Flow Web was broken by a change — ⛔ DO NOT MODIFY these components |
| Lab Ordering (Junction) | 🟢 Stable | — | functions_junction.py | Webhook at junction_webhook; results in clinicsv2/{clinic}/lab_orders |
| Lab Registry | 🟢 Stable | — | metric_registry.py, get_lab_registry HTTP fn | Clinic synonyms at clinicsv2/{clinic}/settings/labSynonyms |
| Lab Visualization | 🟢 Stable | — | ClientDetailPage.tsx → LabsTab | Reference ranges age/gender-aware when available |
Next: Unified clinical document extractor (Phase 1.5 in ROADMAP.md).
Chat & Messaging
| Sub-feature | Status | Last Changed | Key Files | Known Issues |
|---|---|---|---|---|
| Staff ↔ Client Chat | 🟢 Stable | — | functions_chat.py, ChatDrawer.tsx | humanChatEnabled / humanChatClientReplyEnabled flags control access |
| Push for Chat | 🟢 Stable | — | functions_notifications.py | FCM triggers on new messages; device token at users/{uid}.deviceToken |
| AI Chat (client) | 🟢 Stable | — | stub_service_chat_ai_backend.dart | aiChatEnabled flag; aiDefaultFallbackPrompt for KB misses |
| Chat Blast (#691) | 🟢 Stable | 2026-08-16 broadcast a message to a filtered audience (membership/status/payer/service-behavior), 2-step review, sent history + duplicate | functions_chat_option_a.py (chat_blast, chat_blast_count, list_blast_recipients, list_blasts, _blast_author), components/chat/BlastTab.tsx, messages/page.tsx | Sends as the clinic ("Care Team"), pushes to recipients, writes into each client's own thread so staff aren't spammed. History at clinicsv2/{clinic}/blasts. See docs/features/chat-blast.md. |
Next: WhatsApp Business channel (Phase 3 in ROADMAP.md). Twilio SMS messaging (Phase 5).
Copilot (Atlas AI — Staff)
| Sub-feature | Status | Last Changed | Key Files | Known Issues |
|---|---|---|---|---|
| Agent Core | 🟢 Stable | 2026-03 Claude Opus 4.6 + save_clinical_note tool | functions_ai_agent.py, functions_orchestrator.py | Always use fire.db — never create separate Firestore client |
| Quick Answers | 🟡 Active Dev | 2026-03-04 complex query bypass | quick_answers.py | _is_complex_query() detects multi-metric/protocol/report queries and skips QA → routes to full agent. Simple single-metric queries still fast-pathed. |
| Knowledge Base | 🟢 Stable | — | functions_ai.py → kb_search | Stored in clinicsv2/{clinic}/settings/copilot.knowledgeBase[] |
| Orchestrator | 🟢 Stable | 2026-02 intent classification | functions_orchestrator.py | Intent: CLINICAL/OPS/RESEARCH/MIXED → filters tools accordingly |
| AI Scribe (Telehealth) | 🟢 Stable | 2026-03 | functions_scribe.py, generate_soap_from_transcript tool | Recall.ai bot joins video calls → transcript → Claude Sonnet SOAP note with patient context (conditions, meds, allergies, recent notes). Sessions at clinicsv2/{clinic}/scribe_sessions. Gap: In-person ambient scribe via Recall.ai Desktop SDK (Phase 2.6). |
| Save Clinical Note | 🟢 Stable | 2026-03 new tool | functions_ai_agent.py → _exec_save_clinical_note | Copilot can save notes directly to patient chart (requires clinician confirmation) |
| Streaming | 🟢 Stable | — | clinical_agent_stream, RTDB debug.actions | Use RTDB debug to verify tool routing path |
| Patient Memory | 🟢 Stable | — | functions_ai_memory.py | Stored at clinicsv2/{clinic}/clinic_users/{uid}/settings/memory |
| Atlas REST API | 🟢 Stable | 2026-07-10 audited | functions_atlas_api.py | API key auth (X-API-Key/Bearer), gen/validate/revoke, clinic scoping, rate limits. Settings UI EXISTS (Settings → API & Webhooks). Scopes ENFORCED as of 2026-07-10 (#182): 'clinical'/'admin' gates PHI tools, 'atlas'/'admin'/'operations' gates writes, else read-only — hard tool-level filter (filter_tools_for_api_key); legacy scope-less keys grandfathered full access. |
| MCP Server | 🟢 Stable | 2026-07-10 audited | functions_mcp.py | MCP 2024-11-05, JSON-RPC 2.0; 14 read tools + atlas_query meta-tool; shares the REST API key store (managed in the same Settings UI). Key scopes enforced at tool level (#182, shares filter_tools_for_api_key). Gap: tool calls route through the agent (latency) instead of direct bindings. |
| Test Suite | 🟢 Stable | 2026-02 6/7 passing | functions/test/test_copilot.py | Sleep query fails (test API key needs clinical permission) |
| SSE Streaming + Extended Thinking | 🟢 Stable | 2026-06 | clinical_agent_sse (functions_ai_agent.py) | Live reasoning + elapsed timer in basisflow-web Atlas panel. |
| Observation Engine (Need Attention) | 🟢 Stable | 2026-07-21 heartbeat-crash fix | functions_ai_agent.py, heartbeat dispatcher/worker | Proactive scan→score→ground→dashboard briefing card. Detectors: templates / clinician-rules / autonomous. Toggles in Settings→Need Attention. ⚠️ 2026-07-21: was crashing every ~15 min (firebase_functions.logger.warning AttributeError) → engine dormant fleet-wide after #371 broadened the scan; fixed (logger.warn), verified clean. |
| Client Apps — In-focus metric (heroScore) #564 | 🟡 Built, pending platform deploy | 2026-07-22 | settings/page.tsx (ClientAppTab), preferences/clientApp.heroScore | Selector to choose/hide the home-screen hero score (biologicalAge/recovery/cardiovascular/adherence/none); mobile already reads it (rp_live.dart). Mirrors slotGrouping. |
| Chat Draft-Assist + Auto-Categorize | 🟢 Stable | 2026-06 | functions_ai_agent.py | "✦ Draft with Atlas" in chat; incoming messages auto-categorized. |
| Scheduled Tasks | 🟢 Stable | 2026-06 | schedule_task tool, heartbeat | One-time + recurring reminders/messages/reports ("every Friday send me a revenue report"). |
| Clinic Analytics Engine | 🟢 Stable | 2026-06 | functions_reports.py (generate_report, run_report) | NL reports: revenue/sessions/orders/clients. Nightly rollups + live fallback. |
| Knowledge-Gap Loop | 🟢 Stable | 2026-06 | gap-loop capture + weekly digest | Captures what Atlas couldn't answer → weekly digest. |
| Atlas in Slack — AI Employee | 🟢 Stable | 2026-07 | functions_slack_bot.py, functions_atlas_slack.py, atlas_slack_worker/events/interactivity | Ops Q&A + confirm-card approvals as Slack buttons. Per-clinic install (state=clinic_id). PHI denylist gate + Settings→Atlas AI toggle. Identity via userMap→email→pending. |
| Atlas Ops Write-Tools (Slack + web) | 🟢 Stable | 2026-07 | functions_ai_agent.py (_call_clinic_handler, _COPILOT_ACTION_WRITERS) | Confirm-card gated: invite client/staff, time-off, products, services, memberships, duplicate (service/membership/protocol), tasks, reschedule/reassign. Bright lines: never message patients, never charge. |
| Atlas Services & Memberships (with Stripe mint) | 🟢 Stable | 2026-07 | functions_ai_agent.py (_mint_stripe_price) | Create/update/duplicate services + memberships; mints Stripe product+price on the clinic's connected account (execute_copilot_action/atlas_slack_interactivity carry STRIPE_API_KEY). |
| Goal-Gate (heartbeat evaluator) | 🟢 Stable | 2026-07-10 | functions_heartbeat.py (_goal_gate, _default_criteria) | #157: fresh-context Haiku evaluator judges each heartbeat/scheduled run against successCriteria (per-task or per-type defaults) before the result ships; 1 retry with feedback, then ships flagged lowConfidence. Verdicts → atlas_signals (stream gate). Fails open. ⚠️ Deploy landmine: helpers must NOT sit between @on_schedule and the dispatcher def (decorator displacement deleted the dispatcher once — restored). |
Next: Goal-based evaluator stop-gate (autonomy prerequisite) · continual-learning signal capture (confirm-card accept/edit) · consumer Atlas. See docs/claude/atlas-operator-plan.md.
Programs & Protocols
| Sub-feature | Status | Last Changed | Key Files | Known Issues |
|---|---|---|---|---|
| Protocol Assignment | 🟢 Stable | — | functions_clinic.py → assign_protocol | Dual-write: clinicsv2/.../protocols + users/{uid}/protocols |
| Event Generation | 🟢 Stable | — | functions_clinic.py → assign_protocol | Events get protocolId, habitId, clinicId — needed for preset lookup |
| Client Completion Sync | 🟢 Stable | — | functions_event_mirror.py → sync_user_event_to_clinic | Firestore trigger; needs clinicId on event to find clinic path |
| Preset Display (Hybrid) | 🟢 Stable | — | route_summary.dart → _loadRecurringEventPresets | Looks up clinicsv2/.../protocols/{id} → finds habit by habitId |
| Biomarker Tracking | 🟢 Stable | — | lib/biomarker-catalog.ts, BiomarkerProgressCard.tsx | 80% adherence required for valid analysis |
| Copilot Protocol Context | 🟢 Stable | — | lib/protocol-copilot-context.ts | "Ask Copilot" button in PlanTab builds full context |
| Plan of Care — Service Visits (#673) | 🟢 Stable | 2026-08-16 protocols can include Service Visit activities (a booked clinic service with a target # of visits) alongside habits/supplements; Plan tab shows a mixed plan card | functions_clinic.py (book_care_plan_series shared planId, targetVisits/closeReason, appointment↔activity auto-complete bridge), model_event.py (Event.planId), PlanTab.tsx (attended/target counters, close-with-reason, flat chronological calendar), lib/protocol-data.ts (Service Visits type) | Booked visit series shares one planId; per-visit .ics confirmation via send_care_plan_confirmation_email. See docs/features/plan-of-care.md. |
Next: Voice interface for Atlas (Phase 4 in ROADMAP.md). Ambient scribe improvements (Phase 2.6).
Notifications & Email
| Sub-feature | Status | Last Changed | Key Files | Known Issues |
|---|---|---|---|---|
| Appointment Emails | 🟢 Stable | — | functions_appointment_emails.py | iCal attachments included; branded domain via MailerSend |
| Email Delivery Tracking | 🟢 Stable | 2026-03 | functions_email_webhooks.py | MailerSend webhook → clinicsv2/{clinic}/clinic_users/{uid}/emailEvents. Tracks delivered/bounced/spam. Correlates by recipient + subject + timestamp. |
| Push (Appointments) | 🟢 Stable | — | functions_appointment_notifications.py | Device token at users/{uid}.deviceToken |
| In-App (Hybrid) | 🟢 Stable | — | service_notifications.dart | iOS 64-notification limit handled; timezone-aware scheduling |
| Guest Invite Email (#485) | 🟢 Stable | 2026-08-17 a guest added to a booking (name+email) is emailed a branded invite + .ics AND captured as a CRM lead | functions_appointment_emails.py → send_guest_invite_email, functions_clinic.py → _process_event_guest (from book_appointment), functions_growth_leads.py → create_referral_lead (source_id='event_guest') | Works from app, client portal, and staff booking. Guests without an email are recorded but not emailed/leaded. See docs/features/guest-invites.md. |
Next: Twilio SMS reminders (Phase 5 in ROADMAP.md). WhatsApp Business API (Phase 3 in ROADMAP.md).
Onboarding
| Sub-feature | Status | Last Changed | Key Files | Known Issues |
|---|---|---|---|---|
| Client Invitation | 🟢 Stable | — | functions_clinic.py → add_clinic_user | Initializes onboarding state, sends welcome email |
| Intake Forms | 🟢 Stable | — | functions_onboarding.py | Forms at clinicsv2/{clinic}/settings/onboarding; rendered at forms.basishealth.io |
| Mobile Onboarding | 🟢 Stable | — | route_onboarding.dart | 9 pages: privacy → health → analyze → devices → profile → sleep → windows → notifications → calendar |
Next: Client self-service portal onboarding (basisweb/app/portal/onboarding/ in progress).
Marketplace & Payments
| Sub-feature | Status | Last Changed | Key Files | Known Issues |
|---|---|---|---|---|
| Stripe Payments | 🟢 Stable | 2026-03-15 invoice billing proration fix | functions_payment.py, PaymentTab.tsx | ACH + card. Invoice billing with billing_cycle_anchor (1st of month). Proration estimate uses clinic timezone (not browser local time). Removed off_session=True conflict with setup_future_usage. 3-column Charge Client modal with dedicated cart column. |
| Client A/R Ledger & Balances (#710) | 🟢 Stable | 2026-08 per-client running balance (accounts-receivable): post charges + settle by card-on-file / hosted invoice / manual (cash·check·Venmo·Zelle); post insurance patient-responsibility to the balance without double-charging a collected copay | functions_payment.py (post_ledger_charge, add_ledger_charge, settle_ledger, post_remittance_to_balance, list_ledger, void_ledger_entry), PaymentTab.tsx (ClientLedgerSection, "Balance") | Ledger at clinicsv2/{clinic}/clinic_users/{uid}/ledger. settle_ledger methods: card_on_file / invoice / manual. Callables gated ClinicPermission.BILLING. Scheduled auto-invoice emails open balances. See docs/features/billing-insurance.md. |
| ACH / Bank Accounts | 🟢 Stable | 2026-03-04 | PaymentTab.tsx, PaymentModal.tsx | Bank accounts show with Building2 icon. ACH charges return processing status — staff sees "bank transfer takes 3-5 days" toast. Client portal shows success-like state for processing. PaymentElement auto-shows bank entry once backend enables us_bank_account. |
| Memberships | 🟢 Stable | 2026-03-15 | clinic_service, Basis Flow Web Marketplace tab | view_revenue permission is standalone — not implied by billing |
| Products | 🟢 Stable | — | Basis Flow Web Marketplace tab | services permission separate from products |
| Marketplace Catalog Variants | 🟡 In progress | 2026-05-06 | functions_clinic.py (marketplace_*, add_product, update_product), CatalogAdminPanel.tsx | Per-variant vendorCost, msrpPrice, platformFeePctOverride (admin), markupPctOverride (admin or clinic). Computed wholesalePrice/suggestedRetailPrice persisted at save time. Field rename variants → variantGroups (legacy still read for one release). Storefront variant selector (basisweb/basishybrid) and Stripe code-naming for restricted categories pending. |
| Fullscript | 🟢 Stable | — | functions_fullscript.py | OAuth per-clinic; orders webhook at fullscript_webhook |
| Referral Rewards (#719) | 🟢 Stable | 2026-08-17 clinic toggles referral rewards, picks Fixed amount or % of referred client's first payment; referrer earns a balance credit on conversion, applied to Stripe balance with one button | functions_growth_leads.py (award_referral_on_conversion, _resolve_reward_dollars, _post_referral_credit), functions_growth_crm.py (referral_config, mark_referral_successful), functions_payment.py → apply_ledger_credit_to_stripe, settings/page.tsx (GuestsReferralsTab) | Reward posted once (idempotent) to the referrer's ledger. See docs/features/referral-rewards.md. |
| Book & Pay Later (#656) | 🟡 Partially live | 2026-08-17 staff setting + backend shipped; client-app "pay now / pay later" buttons still rolling out | functions_clinic.py (book_appointment pay-later path: validates allowPayLater, stamps paymentStatus/unpaidAmountCents, posts ledger charge), functions_payment.py → post_ledger_charge (source='booking_pay_later'), model_event.py, settings/page.tsx (allowPayLater toggle, Client Apps → Payments), EventDetailsDrawer.tsx ("Unpaid — $X owed") | Pay-later posts the cost to the client ledger; appointment flagged Unpaid. Toggle at preferences/clientApp.allowPayLater. See docs/features/book-pay-later.md. |
Next: Twilio SMS add-on (Phase 5 in ROADMAP.md). Basis Hybrid status gating UI (needs app release).
Permissions & Auth
| Sub-feature | Status | Last Changed | Key Files | Known Issues |
|---|---|---|---|---|
| RBAC | 🟢 Stable | — | functions_permissions.py, permissions/{clinic}/users/{uid} | ALWAYS test with Admin role, NOT System Admin |
| Custom Roles | 🟢 Stable | — | clinicsv2/{clinic}/settings/customRoles | Each settings doc needs explicit Firestore rule |
Next: Granular per-feature permissions for add-ons (e.g., Twilio SMS access).
Client & Staff Management
| Sub-feature | Status | Last Changed | Key Files | Known Issues |
|---|---|---|---|---|
| Archive/Delete Workflow | 🟢 Stable | 2026-03-15 | ArchiveDeleteModal.tsx, ClientDetailPage.tsx, StaffDetailPage.tsx, functions_clinic.py | HIPAA-compliant archive (10-year retention) + permanent delete for test accounts. Shared modal with step flow (choose action → confirm → result). Archive sets status to archived, cancels Stripe subscriptions, optional data export. Hard delete requires typing "DELETE", recursively removes all subcollections. Staff archive removes from public_clinicians. Replaces window.confirm(). |
| Client Status Gating | 🟢 Stable | 2026-03-15 | functions_clinic.py, BookingDrawer.tsx, ClientDetailPage.tsx | Backend blocks inactive/paused/archived booking. Auto-reactivation cron for paused clients. Scheduled deactivation with orange banner. Basis Hybrid: TODO — needs app release to hide booking UI. |
Next: Basis Hybrid status gating UI (needs app release).
Growth Suite
| Sub-feature | Status | Last Changed | Key Files | Known Issues |
|---|---|---|---|---|
| Website Builder (V2 — componentized) | 🟡 Active Dev | 2026-08-20 rebuilt from raw-HTML generation to componentized sections: the generator emits a structured sections[] (hero/method/services/pricing/team/testimonial/cta) filled from REAL clinic data + AI copy; basis-sites renders them through locked-layout _shared components — AI writes copy + picks images, components own the layout (no spacing/button/placeholder drift). ALL pages componentized. Plus generation metering: first generation free, then generationCredits — gates NEW full generations only (never editing/refining an existing site or anything else about the clinic). | functions_growth_website.py (generate_website_v2, _generate_hero, _generate_sections, get_website_credits, grant_website_credits), hybrid/basis-sites/templates/TemplateRenderer.tsx + templates/_shared/* (Hero/Services/Pricing/HowItWorks/Team/Testimonials/CTA), app/preview/[clinicId]/page.tsx, storage clinicsv2/{clinic}/growth_website/main (+ /pages) | Componentization + metering built, pending deploy (wt/growth batch). Deploy = functions:generate_website_v2,get_website_credits,grant_website_credits + basis-sites (Netlify). Refinement is still HTML-based → moving to section-edit. Next: GitHub eject/headless — one-way export to a repo that stays data-connected to Basis (services/booking/chatbot live) (#653); Stripe buy-credits + Settings→Billing surface, meter via #536 (#779). |
Integrations
| Integration | Status | Last Changed | Key Files | Notes |
|---|---|---|---|---|
| Terra (Health Data) | 🟢 Stable | 2026-07-21 logger landmine cleared | functions_terra.py, terra_cache_*, terra_data_converter.py | Webhook → DuckDB/GCS → Firestore mirror. 2026-07-21: cleared logger.warning→logger.warn crash-landmine in the cache/converter layer (latent, AI-consumption path via functions_ai_integrated — NOT the sync/webhook pipeline). Recurring sync/webhook crashes (DuckDB-in-serverless) remain a separate structural issue — see docs/claude/services/terra.md. |
| Zoom | 🟢 Stable | — | functions_zoom.py, clinicsv2/{clinic}/config/zoom | Per-clinic OAuth |
| Google Meet | 🟢 Stable | — | functions_google_meet.py, clinicsv2/{clinic}/config/google | Per-clinic OAuth + Calendar API |
| Junction Health (Labs) | 🟢 Stable | — | functions_junction.py | Orders at clinicsv2/{clinic}/lab_orders |
| MailerSend | 🟢 Stable | 2026-03 delivery webhook tracking | functions_appointment_emails.py, functions_email_webhooks.py | Branded domains: verify DNS, set useBranded: true. Webhook tracks delivered/bounced/spam. |
| OpenAI / Vertex | 🟢 Stable | — | functions_ai.py, functions-vertex/ | For inference only — no training on patient data |
| Slack Bot | 🟢 Stable | 2026-03 support bot | functions_slack_bot.py | Support bot for provider queries. Per-clinic Slack workspace integration. |
| Outbound Webhooks | 🟢 Stable | 2026-03 | functions_webhooks.py | Fires events (note.created, lab.created, appointment.created/cancelled, client.created, error.logged) to configured endpoints. Config: clinicsv2/{clinic}/settings/webhooks. |
| DoseSpot (e-prescribing) | 🟢 Stable | 2026-03-13 address dict fix | functions_dosespot.py, components/dosespot/DoseSpotEmbedModal.tsx | Full integration: clinic provisioning, clinician NPI/DEA registration, patient sync, SSO iframe embed, Rx pull-back, allergy management, pharmacy search (Surescripts). 12 Cloud Functions. Gap: Prescriptions not shown in Plan tab (only synced to Firestore). Notification counts not surfaced in UI. See ROADMAP Phase 9. |
| Atlas REST API | 🟢 Stable | 2026-03 | functions_atlas_api.py | External agent access via POST with API key auth (X-API-Key or Bearer). Key generation/validation/usage tracking. Routes to run_claude_agent. |
| MCP Server | 🟢 Stable | 2026-03 | functions_mcp.py | MCP 2024-11-05 spec. API key auth, permission-based tool/resource access. Supports Claude Desktop, Cursor, etc. 766 lines. |
| Clinical Extractor | 🟢 Stable | 2026-03 | functions_clinic.py → extract_conditions_goals | Scans 5 sources (docs, intake, labs, chats, health data). GPT-4o-mini. Auto-detects abnormal labs. Phase 2: expand to medications, procedures, social/family history. |
| KB Auto-Learning | 🟢 Stable | 2026-03 | functions_kb_sync.py | Firestore triggers auto-populate kbAuto/kbAutoStaff from clinic data (services, products, memberships, staff). Atlas kb_search() includes auto entries. |
| PostHog Analytics | 🟢 Stable | 2026-03 | components/PostHogProvider.tsx, lib/posthog.ts | HIPAA-safe config: maskAllInputs, no IP capture, identified_only profiles. Both basisflow-web and basisweb. |
Next: Twilio integration (Phase 5 in ROADMAP.md). WhatsApp Business API (Phase 3).
Data Patterns (Cross-Cutting)
| Pattern | Status | Key Rule | Last Updated |
|---|---|---|---|
| Date-only fields | 🟢 Stable | Store as string "YYYY-MM-DD" — NEVER Timestamp | 2026-01 |
| Pydantic ↔ Firestore | 🟢 Stable | Use BeforeValidator for type coercion; Timestamp/string can both appear | 2026-01-25 |
| camelCase fields | 🟢 Stable | All Firestore fields camelCase — Python models too (no snake_case aliases) | — |
| Firestore rules | 🟢 Stable | Add rule BEFORE writing code to new path; each settings/{name} needs own rule | — |
| Dual-write (protocols) | 🟢 Stable | clinicsv2/.../protocols is source of truth; users/{uid}/protocols is mirror | — |
Appointment Data Gotchas (Frequently Causes Bugs)
| Field | ✅ Correct | ❌ Wrong |
|---|---|---|
| Location name | data.locationName || data.location?.name || data.location?.text | locationName only |
| Coach UID | attendees[isOrganiser].profileId | coachUid (may not exist) |
| Coach name | Look up clinician doc by UID | attendee.name — may be an email address |
Bug Context Protocol
When debugging a feature, follow this order:
- Find the feature row above — check Known Issues first
- Read the Key Files — understand current implementation before changing
- Check Known Gotchas in
CLAUDE.md → Known Gotchas & Danger Zones - Use debug tools in
basis-functions/tools/before declaring fixed - Update this file when done — change status and last changed
Changelog
| Date | Changes |
|---|---|
| 2026-03-04 | Added: ACH bank account payments (backend + PaymentTab + PaymentModal). Scheduled deactivation + auto-reactivation cron. Copilot complex query bypass. AI Scribe, save_clinical_note tool, Slack bot, outbound webhooks, email delivery tracking, activity log — reflected from recent commits. Versioning table. Next-phase notes per feature section. |
| 2026-03-06 | Added: DoseSpot e-prescribing, Atlas REST API, MCP Server, Clinical Extractor, KB Auto-Learning, PostHog Analytics, CI/CD Pipeline, Health Check. Updated AI Scribe status (telehealth stable, in-person ambient pending). Updated Atlas API/MCP entries with current file locations. |
| 2026-03-08 | Added: Clinical Audit Trail (functions_clinical_audit.py), Superbill Auto-Generation (functions_superbill.py), Provider Utilization table in analytics dashboard. Fixed nightly orchestrator to use AGENT_PAT. Added clinic_id to log_bug endpoint. ROADMAP: Added Phase 6 (Clinical Forms Scheduling) and Phase 7 (Atlas AI Custom Reports). |
| 2026-03-15 | Added: Archive/Delete workflow (ArchiveDeleteModal, HIPAA 10-year retention, recursive subcollection delete). Stripe invoice billing proration timezone fix (clinic TZ, removed off_session conflict, 3-column cart modal). Multi-location coach availability (checkbox selection, deduplication). Moved Client Status Gating to Client & Staff Management section. Memberships → Stable. |
| 2026-08-17 | Added weekend ship batch: Plan of Care / Service Visits (#673), Chat Blast (#691), Guest Invite Email (#485), Link appointment to a program (#257), Referral Rewards (#719), Book & Pay Later (#656, partially live), Open-Gym / QR Check-In (#721). Each has a docs/features/*.md how-it-works page and a docs/claude/growth-content-backlog.md row (🆕 unannounced). |
Maintained by agents and humans. Update after every feature change.