மாற்று பதிவேடு
என்ன புதிது.
அது கப்பல்கள் போல், தளத்தில் ஒவ்வொரு மேம்பாடு.
· SecurityService
The revenue cycle worklists, and the one restriction that outranks them
Every list here exists because its failure mode is a MISSING row rather than a wrong one, which is why none of them show up in an ordinary report. A claim a clearinghouse rejected never reached the payer: it is in no denial report because nobody denied it, in no payment report because nobody paid it, and its status changed once, quietly, weeks ago. Practices lose more money to that silence than to denials they fought and lost. Timely filing is the only absolute deadline in the cycle — past it the money is gone with no appeal — and it counts rejected claims as unfiled, because a claim a clearinghouse stopped has not been filed however many times it was sent. A procedure performed and never charged raises nothing at all, since the charge was simply never created. Money that arrived and cannot be attributed reconciles perfectly the moment somebody adjusts it away, which is exactly how a ledger stops being trustworthy. Denials are grouped by CARC code and CPT rather than listed, because one procedure denied twenty times for one reason is a fixable pattern and the same twenty listed individually are twenty chores. mayBillInsurance is the exception: not a worklist, a refusal. A §164.522(a)(1)(vi) restriction is MANDATORY — when a patient pays out of pocket in full, a covered entity must agree not to tell their health plan — and billing anyway is a violation that cannot be cured, because the disclosure has already happened and there is no recalling a claim a payer adjudicated. It answers with a REASON rather than a boolean, because "this cannot be billed" with no explanation invites somebody to work around it. A restriction that limits who may be TOLD about a visit does not block billing it; conflating the two would refuse claims the practice is entitled to file. Outstanding excludes the patient's share throughout: chasing a payer for a copay is how a practice annoys a payer and never bills the patient. And an unadjudicated claim owes nothing YET rather than its full charge — reporting the charge would overstate every practice's receivables. 1235 tests green.
· kamo-shared-library
Count new bug reports under the listing's own visibility rule
The badge and the list it opens are read side by side; a count computed under a different tenancy rule than the list is a bug report waiting to happen.
· kamo-shared-library
Count missed calls a member has not been shown
callResult is a free-form provider string, not an enum — the history tab matches the literal 'Missed', and upper() is what stops a provider that reports MISSED from silently never counting. Voicemails are counted per instance, matching the set the softphone tab actually lists, so the badge and the list cannot disagree.
· kamo-shared-library
Record whether an attendee has been told
The case this exists for is a webinar booked on somebody's behalf by somebody else: registration was recorded and nothing in the app ever told them. status could not stand in — REGISTERED is the state of the booking, not of awareness.
· kamo-shared-library
Count assistant replies the member has not seen
Keyed by session GUID because that is what a tool window carries. ASSISTANT only: a member's own prompt is not news to them.
· kamo-shared-library
The healthcare engagement vocabulary
An appointment is the INTENT — a slot someone holds and may not keep — and the ENCOUNTER is the commitment: the visit that actually happened, the thing a note is written against and a claim is billed from. Calling both "appointments" is how a practice loses the ability to say how many people it actually saw, which is the number every other number depends on. The catalogue is Visit Types rather than Products, because what a practice sells is time with a clinician in a named shape: a new-patient consult, a wound check, a Mohs stage.
· kamo-internal
Give bugs and enhancements their own support section
The two report entries were sitting under "Online Support" beside Contact Support and View Active Tickets, which is a different errand: those two are asking a person for help, these two are telling us the product is wrong. They were there because there was nowhere else to put them. New "Bugs & Enhancements" section between Online Support and Training, with reporting first — that is what somebody opens this menu to do, and reading the list is where they end up afterwards: Report Bug / Request Changes View Latest & Past Reports Both labels read through renamed keys. "System bugs" was always the developers' name for the thing; what a member is looking at is their own reports, and what they came to do covers a request for a change as much as a defect — which the form has offered since the enhancement type shipped, without the menu ever saying so. No empty-heading risk: NavToolButton, LauncherParentCard and PinnedAppLinks all already drop a section with no items, so a member holding none of these rights sees no heading rather than a bare one. The nav and the home launchpad render from this one hook, so both surfaces move together.
· kamo-internal
Member-level availability, with sub-tabs and a calendar layer
**************** now has two sub-tabs. "Webinar Types" is what an administrator configures — the offering, its presenters, its reminders. "Scheduled Availability" is what a MEMBER declares: when they can host, once, for every type that lists them. Those are different jobs done by different people, and they used to be tangled together in a per-type tab where the same hours had to be re-entered for each offering. The availability tab defaults to yourself. A member picker appears only for MANAGE_WEBINAR_SCHEDULES holders, and the backend enforces the same rule. The type detail view's Slots tab becomes a read-only Availability preview. Nothing is editable there on purpose: one member's windows serve every type they present, so editing them from inside one type would silently change what the others offer. /calendar gains an availability layer — a green backdrop on the week and day grids, a dot in the month cells, and a toolbar toggle. It is painted as a backdrop rather than as events because that is what it is: a window puts nothing on anyone's calendar and is free to sit underneath a meeting, and the meeting is what blocks that part of it out when somebody tries to book. Clicking opens an availability editor, deliberately not the event dialog. availabilityWindows.ts mirrors the server's expansion so the backdrop can be drawn without a round trip per week. The server stays the authority for anything that books; this only paints. The schedule wizard now posts an absolute instant. It previously sent a slot id plus a bare "09:00" to a field the server parses as an Instant, so that path could only ever have thrown. Child-org restrictions move from a dated slot to a weekday, matching the backend: naming one date left every date the parent added later unrestricted. 3025 tests pass; the i18n key guard is clean against a fresh dictionary clone.
· SecurityService
Seed MANAGE_WEBINAR_SCHEDULES from MANAGE_SUPPORT_SETTINGS
Until availability moved onto the member, MANAGE_SUPPORT_SETTINGS holders were the one group who set it — they authored the dated slots on each webinar type, which is exactly the power the member picker on the new Scheduled Availability sub-tab now carries. Without the seed the refactor reads as a removal: a support administrator who could schedule presenters on Friday would find on Monday that they could only schedule themselves. Only the picker is seeded. Editing your OWN availability needs no right at all.
· InitializerService
Create member_availability_window, retire the dated slot table
Creates member_availability_window, adds **************** and rebuilds webinar_child_org_restriction around the type plus an optional weekday. Drops webinar_availability_slot. The old rows are DROPPED rather than migrated, deliberately. A dated slot on a type does not carry the one fact the new table needs — which presenter it belonged to. A type with three presenters could have meant any of them, all of them, or a rota, and every way of guessing is wrong in a way nobody would notice: fan the row out to all three and the platform starts offering visitors times two of them never agreed to. Members re-declare their hours once instead. Restrictions are dropped before slots: the old restriction table holds a foreign key into the slot table, so the other order fails on the constraint. Enum columns are VARCHAR holding the enum NAME, never an ordinal — Hibernate freezes CHECK (col BETWEEN 0 AND N) at create time and never widens it, and removing a value from the middle silently relabels every row below it. Apply this BEFORE MediaService ships the entities that map these tables. KamoInitializerService has no CI, so that ordering is a deploy step.
· MediaService
Resolve bookable times from member availability
Availability is now computed rather than stored. MemberAvailabilityService owns the arithmetic: expand each presenter's declared windows over the type's booking horizon, subtract the webinars they are already presenting and everything on their calendar, apply any child-org narrowing, then keep the start times a webinar of that length still fits inside. Overlap between a window and a calendar event is legal on write and only ever subtracts on read. That is what lets a member declare standing hours once and never revisit them when a meeting lands inside — the meeting removes its own part of the window and nothing else. Candidate start times come off the DECLARED window, not off the free remainder. Stepping from each surviving fragment would turn a clean 9:00 / 9:30 / 10:00 grid into 10:15 / 10:45 for the rest of the morning because of one 15-minute call; generating the grid from the window the member drew and keeping only what still fits means the 10:00 disappears and 10:30 stays put. There is now ONE implementation. The public booking page and the signed-in scheduler each had their own copy of this, with different rules, so a visitor and a member could be shown different times for the same webinar. Both booking paths now also require the requested period to be inside declared availability. Before, that was true by construction because the caller picked one of the type's own dated rows; with an absolute instant on the wire, nothing stopped a request naming three in the morning on a day nobody had offered. /types/{id}/slots/available is replaced by /types/{id}/bookable-times. The public endpoint keeps its path and its availableTimes key, and drops the slots half — it reported a local date and a bare wall-clock time with no zone, which an anonymous browser had no way to resolve into anything it could act on. 30 tests cover the interval arithmetic and the expansion, including the timezone, date-line, daylight-saving and past-midnight cases. 280 pass.
· kamo-shared-library
Move availability from the webinar type onto the member
Availability was webinar_availability_slot: a dated (date, start, end) row owned by a WebinarType. A presenter who ran three kinds of session had to declare the same Tuesday morning three times, and the copies drifted the moment one was edited — the platform would happily offer a visitor a time the presenter had already withdrawn somewhere else. Capacity to host is a fact about a PERSON, so it now lives in MemberAvailabilityWindow, declared once and read by every type that lists that member as a presenter. Windows also recur: "Tuesdays 9 to noon" is one row with a weekday and an optional effective range, not a row per Tuesday forever. Three consequences worth calling out: - WebinarType gains BOOKING_HORIZON_DAYS. A recurring window has no last date, so something has to say how far ahead the booking page looks; without it the resolver would expand a weekly window without end. - Each window stores the IANA zone it was AUTHORED in. Resolving the zone at read time would mean a member updating their profile timezone silently moved every window they had ever declared, including ones already on offer. - WebinarChildOrgRestriction re-anchors from a dated slot to the TYPE plus an optional weekday. It is written by the CHILD org to narrow what its parent offers its staff, and pointing at one date meant every date the parent added later arrived unrestricted — silently reverting to wide open. CalendarEventRepository gains findBusyForMemberInRange, which is what actually blocks out a window. It replaces a creator-only query that also counted cancelled events and decoderd invitations as busy, so a presenter with a diary full of other people's meetings read as completely free. New right MANAGE_WEBINAR_SCHEDULES (241) gates managing SOMEBODY ELSE's availability. Editing your own needs no right at all. Note: RoleRightType.java and RoleRightHierarchyTest.java are shared with an in-flight clinical-rights change already present in the working tree. The audited totals at HEAD expected 184 children against an enum that had 167, so that test was red before this commit; the combined state is green (1843 tests).
· kamo-internal
The Patient Records surface — day, safety, search and chart
A nav entry, four panels and a chart. The design decisions worth reading are the ones where the screen is a control rather than a view. The chart's allergy banner has THREE states. "No known allergies — reviewed", "Allergies not reviewed", and the list itself. An empty allergy list means one of two very different things — no known allergies, or nobody asked — and only one of them is safe to prescribe against. That is the entire reason "no known drug allergies" is a documented statement in medicine rather than an absence of data, and rendering the second as the first is the quiet substitution that ends in an anaphylaxis report. Medications are shown as prescribed AND patient-reported, separately, both present. A prescription says what a clinician intended; a reported medication says what the patient is actually doing, including the drugs from other practices, the OTCs, the interacting supplements and the prescribed drug they stopped three weeks ago without telling anyone. Reconciliation is the act of comparing the two, and a screen that merges them into "medications" makes that impossible while looking tidier. The safety tab is five counts, and a zero renders as GOOD NEWS rather than as an empty list — a queue that shows nothing when it is clear is a queue people stop looking at. Every one of those five fails silently: an order placed and never resulted produces no error and fills no inbox, a critical value nobody acknowledged looks identical to one everybody saw, and a result withheld from a patient looks exactly like one nobody has got to. Each queue settles on its own so a single failing endpoint cannot blank the other four. The waiting room leads the day tab, because it is the only thing on that screen happening right now, and the wait is coloured before it becomes a complaint. It re-renders on a timer: a wait time that only updates on reload is a wait time nobody trusts. A denied chart and a missing one look identical, deliberately — the API answers both 404 so a caller cannot walk the id space and learn which patients a practice has. The screen offers the one honest resolution: emergency access, whose notice says it is recorded and reviewed BEFORE it asks for a reason, because the person deciding should be reading that while they decide. purpose of use is sent explicitly on every read and never defaulted. §164.528 excludes treatment from the accounting of disclosures while 42 CFR §2.25 includes it, so the same access belongs in one report and not the other, and only the screen knows why it is open. One proxy route serves the whole clinical surface, and it forwards the query string — almost every clinical read is parameterised by a date, a purpose or a search term, and the per-endpoint copies elsewhere in this app drop it. Timestamps go through parseServerDate rather than `new Date`. The clinical tables are TIMESTAMPTZ so these values do carry an offset, but the rest of this platform sends LocalDateTime with none — a parser that is right for both stays right when a payload changes shape underneath it. 3025 tests green, typecheck clean.
· kamo-shared-library
Give an SMS conversation a read pointer
VoipConversation had no notion of read at all, so an SMS could be shown but never recorded as seen. Null means never looked, which counts all inbound — the honest reading for every row that predates the column.
· kamo-shared-library
Count social conversations as unread
Social sessions were excluded from the unread membership query by a hand-written type list, so a social conversation could never show a count anywhere. They are MediaSessions with a read pointer and a mark-read endpoint already — only the list left them out. The ownership ambiguity rule moves to a pure helper and gains its second case: a social visitor has no member record, so their messages are stored under the connection owner, and for that member 'not written by me' matched nothing. Each entry now declares its sessionType. The snapshot spans three surfaces that belong on three different indicators, and the client was inferring which from whichever windows happened to be open.
· SecurityService
Keep room for the compact mark among a pile of lockups
A straight "best six by score" is the wrong cap for this, and the reason is the shape of real sites: a company's wide lockup appears in the header, in JSON-LD and in Open Graph and scores highly every time, while its square mark exists once, as a favicon, and scores modestly. Six by score is therefore routinely six lockups -- and the wizard's compact slot, which wants the mark, is left choosing between wordmarks that will be squeezed into a navigation bar. Each shape now gets two reserved places before the remainder fills by score. Costs nothing when a site has only one shape (the reservation goes unused and the list is still full) and is the difference between a usable compact rail and a pointless one when it has both. This matters more now than it did: the compact slot accepts any image and converts it, so a raster mark is a real answer rather than one the wizard would have discarded anyway.
· InitializerService
Read pointers for SMS, AI chat, webinars and missed calls
Three of the five conversation surfaces had no server-side notion of read at all. All four columns are nullable with no backfill: null means never looked, which is what is true of every existing row.
· kamo-internal
Fit the report form in the window without scrolling
The form was about 120px taller than the 460x690 window's content area, so every new report opened on a scrollbar. Six changes, each of which also reads better than what it replaced: - "KamoCRM Marketing Website" -> "KamoCRM Website". Three words for one thing, and the longest chip in a row of five — shortening it drops the whole sites row from two lines onto one. Renamed key (site.KAMOCRM_WEBSITE), not an edited value, or the other 21 locales keep the old wording forever. - The sites hint moves beside "Where did you see it?" instead of under it. It qualifies the question rather than introducing the chips, and a parenthetical does not deserve a line of a 690px window. - "What happened?" loses a row (6 -> 5, maximized 10 -> 9). - The screenshot zone gets the same two-column header: icon and "Screenshots" on the left, the drag/paste/browse tip right-aligned opposite it. - Tighter padding on the type cards and 20px off three bottom margins. And the dashed panel now only exists while there is nothing to show. Once a screenshot is attached the thumbnails ARE the control — an empty rectangle under them is a second thing to explain and a second place to click — so adding another is simply the next tile in the row. The whole area stays a drop target, so a drag never has to find the small square. That change made an old gap visible: screenshots already on the report were tracked as a COUNT, so correcting a report with two pictures on it would have shown an empty zone. They are real tiles now, and removable — the endpoint and its ownership check already existed, and the server hides rather than deletes. Removal is optimistic and puts the tile back with the server's reason if it fails, because a thumbnail that lingers for a round trip reads as a missed click. Estimated at 460px wide: ~467px of content against ~565px of room, so a fresh report has roughly 100px of slack. Five or more screenshots wrap to a second row of tiles and will scroll, which is the right trade.
· kamo-shared-library
The Patient Records rights, and the access rights they are not
Twenty-three rights under ServiceType.EHR, and every one of them is a SECOND gate rather than the only one. Holding VIEW_CHART does not let a member open a chart: ClinicalAccessService still requires a care relationship with that specific patient. A right says which JOB somebody does; the relationship says whose record they may do it to. Only the pair is safe, because the overwhelming majority of real HIPAA enforcement is workforce members looking up a neighbour or a relative — and in every one of those cases the role is entirely legitimate. The splits are where the thinking is. PRESCRIBE_CONTROLLED_SUBSTANCES is NOT a child of PRESCRIBE_MEDICATIONS. The DEA's EPCS rule makes it a different act: identity proofing, a second authentication factor at signing, and a logical access control naming the individual practitioner. A system where "can prescribe" implies "can prescribe controlled substances" cannot satisfy that and cannot even describe who is authorised. SIGN is separate from DOCUMENT because signing turns a draft into a legal record and a visit into a billable one — and scribes, students and medical assistants write notes they may not sign, which is the entire reason cosignature exists. REVIEW_CLINICAL_RESULTS is separate from placing orders because acknowledging a result is a clinical decision, not an inbox action: it asserts somebody qualified saw an abnormal value and decided what to do. MERGE_PATIENT_RECORDS is its own right rather than an implication of editing demographics, because merging two records is silent, very hard to undo, and produces one chart describing two people. BREAK_GLASS_PATIENT_ACCESS is a ROOT, not a child of VIEW_CHART. It is not a bigger version of viewing a chart, it is the ability to bypass the care-relationship check entirely, and a practice's answer to "who may do that" is a much shorter list than "everyone clinical". REVIEW_EMERGENCY_ACCESS is the other half of the bargain and must land on somebody other than the person who used it. VIEW_DISCLOSURE_ACCOUNTING is a root too: producing a patient's §164.528 accounting enumerates every disclosure the practice made about them, and the people who answer records requests are usually not the people who provide care. Deliberately NOT in **************** That mechanism exists to stop a new right silently REMOVING access that already worked; these gate a surface that did not previously exist, so seeding them would hand clinical access to roles nobody reviewed. Day-one reachability comes from the grant-all Administrator heuristic. Taxonomy ratchet moves 231/167/33/31 to 254/184/35/35.
· SecurityService
The clinical API — patients, schedule, chart, and the gate in front
The access rule is the part worth reading. Being logged in is not a reason to see a chart. The overwhelming majority of real HIPAA enforcement is not outside attackers, it is workforce members looking up a neighbour, a relative, a celebrity, an ex-partner — and in every one of those cases the ROLE is legitimate, the person genuinely is a nurse. What separates a permitted access from a snoop is whether they have a RELATIONSHIP with that patient, which is a question about data rather than about roles. So ClinicalAccessService answers care team, or panel, or an explicit break-the-glass grant, and anything else is refused and recorded as refused, because a refused reach at a chart is a stronger detection signal than a permitted one. Break-the-glass is a bargain, not an escape hatch: the access is allowed because it is unmistakably recorded and because someone reviews it. The grant needs a stated reason (it is what the reviewer reads), expires in an hour (the emergency that justifies it is measured in minutes), and is written BEFORE the read it authorises, so an access taken under it can never be one the record cannot explain. The care-team check runs first, so ordinary care is never filed as an emergency and the review queue stays reviewable. purpose is a required parameter with no default. §164.528 excludes treatment, payment and operations while 42 CFR §2.25 includes them, so a wrong purpose produces a wrong legal document handed to a patient — and it cannot be reconstructed later from a request path. Auditing is per RECORD, not per request. "Someone opened a chart" is true and useless; per-record rows are what let a §164.402 assessment answer whether a named individual's medication list was seen. Search audits every match, because a name-and-birth-date search returns the names, birth dates and MRNs of everyone who matched. Forbidden and not-found collapse to the same 404 at the HTTP boundary. Internally they differ — a denial is audited against a named record — but a caller who can tell them apart can walk the id space and learn which patients a practice has, which is a disclosure in itself. The compliance boundary is the one explicit refusal, at 403, because that fix is a contract rather than a permission and "not found" would send a practice hunting for a bug. Free slots are computed from rules, never stored: materialising a year for forty providers is tens of millions of rows that go stale the moment somebody changes their Tuesday. Booking checks conflicts in the DATABASE, because two schedulers taking the same slot at once is ordinary and an in-memory check has a window the second booking fits through. Overbooking is modelled rather than refused, since clinics deliberately double-book against no-shows and an unmodelled policy moves the workaround somewhere unauditable. EhrEndpointGuardTest is stricter than the platform ratchet on purpose. This service is anyRequest().permitAll() with no method security, so public is the default; the platform ratchet catches a handler that mentions no session helper, but cannot catch one that resolves a session and ignores the answer. For lead data that is a gap — for a chart it is an unauthenticated read of medical records. So it requires BOTH halves in every mapped handler under ehr/, plus that no handler takes a tenant from the caller. Mutation-proven by deleting one isResolved() check. 1211 tests green.
· kamo-internal
A domain is no longer required to create an org
Requiring it here was the wizard's half of the rule this effort removed: a customer could not finish creating an org without naming a domain, and then could not get into it until that domain's DNS resolved. An org is reached on the platform host and by its web alias whether or not it ever has one of its own, so the domain is a white-label nicety. Checked for shape when given and never demanded — the same treatment the contact step already gives its optional fields. The asterisk is gone from the label, the hint says what happens when it is left blank, and a blank value is submitted as null rather than "": an empty string is an absent domain, not a domain named "". The web-alias path is untouched, including that it still ignores a domain left behind in the field by switching between the two.
· kamo-shared-library
The repository layer, with a harness that proves it boots
Repositories for patients, encounters, scheduling, the chart, notes, questionnaires, coverage and claims. Every query leads with tenantId because on YugabyteDB the leading index column decides which tablet serves it, and every query that could grow without bound is either bounded by a code or takes a Pageable — "all observations for this patient" works beautifully in a demo and times out on a diabetic patient of fifteen years. Three rules are enforced by the query shapes rather than by the callers. Resolved problems are excluded in the database, because "the UI filters it" is a promise every future caller has to keep and a resolved condition on an active problem list is a clinician acting on something untrue. Coverage comes back primary-first from the database, because billing the secondary first is a denial that costs a month. Allergy and medication checking is never paged, because a page boundary in the middle of an allergy list is a prescription that missed the allergy on page two. Observation gained a diagnosticReportId, and its index took the slot an encounter index would have had. The trade is deliberate: a chart screen showing one visit already knows the patient, so encounter-scoped reads come off the patient index — but a clinician reviewing a result does NOT know which of a patient's thousands of values belong to the report in front of them, and finding out without that index is a scan of the largest table in the schema. ClinicalQueryParseTest is the part worth reading. A JPQL string is not checked by the compiler, and Spring Data validates annotated queries at BEAN CREATION — so o.codeCode where the mapping says o.code.code compiles, ships, and takes the service down at startup rather than returning nothing. On this platform that is SecurityService crash-looping and every login on it stopping. The harness builds Hibernate metadata over the clinical entities and asks the HQL parser to translate every @Query, and parses every derived finder name with Spring Data's own PartTree. No database is touched: parsing needs the mapping, not a connection, so it costs the build seven seconds and needs no container. Both halves were mutation-proven — o.code.code -> o.codeCode and ...OrderByActualStartDesc -> ...OrderByPeriodStartDesc each fail it with the exact message a startup would have produced. 1834 tests green.
· kamo-shared-library
Insurance, claims, remittance and prior auth (SP14)
Coverage models ORDER, because a patient with two policies has a primary and a secondary and billing the wrong one first is a denial that costs a month and a resubmission. The subscriber is separate from the patient because it usually IS someone else, and payers match on the subscriber's details — a claim filed with the child's name in that field is denied for what reads like a data problem. Benefit amounts are explicitly a CACHE with an as-of date: a deductible met in March is not met after the plan year rolls, and a patient quoted from a stale cache was told the wrong number by their doctor's office. Self-pay is a real coverage row, not the absence of one — §164.522(a)(1)(vi) gives self-pay patients a right that depends on knowing the difference between "pays directly" and "we never asked". Every eligibility check is kept, with the raw 271 beside the parsed fields. When a payer denies a claim saying the patient was not covered, the practice's defence is the response that payer itself returned before the visit; storing only the latest answer throws away exactly the evidence that matters, because the latest answer is the one that agrees with the denial. A technical failure is neither a yes nor a no: treating "payer unavailable" as "not covered" turns away covered patients, and the reverse sees uncovered ones. Claims separate REJECTED from DENIED, and that distinction is most of the value. Rejected means a clearinghouse stopped it and no payer ever saw it; it goes nowhere unless someone resubmits. This is where practices lose money silently — never denied, never paid, simply stopped, with nothing failing loudly. timelyFilingDeadline is stored rather than derived because it is payer-specific and absolute: miss it and the money is gone with no appeal, usually discovered months later. Claims snapshot the identifiers they were billed with, so a patient changing insurance in March cannot retroactively change what January's claim said. Claim lines carry their own status and CARC code because payers adjudicate line by line — a claim reported as paid routinely contains a denied line, and the code determines the work: CO-97 is a coding conversation, PR-1 a patient one, CO-50 a documentation one. One status field cannot hold three different jobs. Remittance keeps unappliedAmount honest. Interest, a takeback for a claim paid last quarter, a payment for a claim this practice cannot find — these are constant, and forcing the remainder to zero by adjusting something is how a ledger stops being trustworthy. Posting to claims and matching the bank are tracked separately, because the second is the half nobody does. PriorAuthorization tracks units used, because authorizations are granted for a COUNT and the thirteenth of twelve is denied — discovered when the denial arrives, after care was delivered that cannot be billed to the payer OR to the patient. 1827 tests green.
· kamo-shared-library
Questionnaires that produce coded data, not form answers (SP8)
Deliberately not the platform's lead form builder, and the reason is the whole design. A capture form's job is to collect answers. A clinical questionnaire's job is to produce CODED CLINICAL DATA: a PHQ-9 answer is not a number in a blob, it is a LOINC-coded Observation with a score, a severity band and usually an obligation to act. A generic form stores what someone typed; a questionnaire has to say what it MEANS in a vocabulary another system can read. Merging them makes the second impossible. Published instruments are frozen and responses cite the VERSION they were answered against. Instruments do get edited, almost always by someone fixing a typo with no idea they have just changed what every historical response meant — and a score read against wording the patient never saw is worse than no score. standardInstrument goes further: PHQ-9 is validated as written, so the editor refuses rather than trusting everyone to know that. criticalResponse exists because of PHQ-9 item 9. A patient disclosing self-harm must not wait for a total to be computed, a queue to be worked or anyone to open the response — that is the one question on the form where the delay IS the harm. The flag is denormalised onto the response so the alert can fire without reading anything. Two silent failures get their own predicates. A scored instrument with no score bands is the commonest way a screener is deployed and then quietly ignored: answers captured, nothing computed, nobody notices until an audit asks for the follow-up rate. And a completed response with no Observations written is how a practice collects screeners for a year and then finds its quality measures report nothing. Partial responses are kept. Patients abandon forms halfway, on a phone, in a waiting room, and discarding what they entered means asking them again from the beginning — which is how a practice teaches people not to bother. The half they finished is often the half that mattered. 1803 tests green.
· kamo-shared-library
Patient identity, and chart documents on the EXISTING doc manager
The EHR gets no second document system and no second signature system. PATIENT_CHART is a new ImageAssocType keyed on the patient's logical id, so a chart is an ordinary document collection the DocManager already renders, and binders already work on it — which is exactly what assembling a records-release packet is. Delete and overwrite are OFF and not org-configurable: a received medical record is evidence, replacing the bytes under a document that a note or a disclosure ledger entry cites rewrites history silently, and removing one destroys the §164.528 accounting the practice must be able to produce. Folders and doc types are required, because a chart of three hundred loose scans is unusable and the doc type is what lets a records request answer "labs from 2024" without a human opening every file. Clinical consents reuse EsignGenericEnvelope — two signature systems means two answers to "prove the patient signed this", and only one survives a subpoena. A §164.508 records-release authorization is its own context type because it is a different instrument: it names a recipient, carries an expiry and can be revoked, and filing it as one more consent is how a practice discloses under an authorization that expired eleven months ago. DICOM, C-CDA, FHIR JSON/XML, HL7 v2 and XDM are storable as themselves. Transcoding a C-CDA to PDF on the way in destroys the structured data that made it worth receiving, and a DICOM study rendered to JPEG is a picture of an image rather than the image. PatientLink makes a merge a LINK and never a delete. The absorbed record stays, tombstoned and pointing at its survivor, because every external system that ever held the old identifier goes on asking about it — a referral, a lab, a payer, an HIE query, a portal bookmark — and answering "that id never existed" turns a merge into data loss on someone else's side. NOT_A_MATCH is as important as the merges: twins share a surname, a birth date and an address, and without a durable record that a pair was reviewed and ruled distinct the matcher resurfaces them forever until a tired clerk merges two different people, which is the most damaging error this module can make and very hard to undo. The merge manifest is stored because an unmerge without one means guessing which of two charts each row came from. PatientIdentifierIndex keeps normalised and phonetic keys OUT of the patient record. Matching runs on normalised values and displaying them is wrong: "O'Brien-Smith" must be findable as "obriensmith" and must still render with its apostrophe. Superseded rows stay searchable on purpose — staff search by the old MRN for years, and the right answer is to land them on the survivor, not to report no such patient. 1789 tests green.
· kamo-internal
Report form becomes a tool window; filter and status changes
REPORT FORM -> POPUP TOOL WINDOW. A modal was the wrong shape for the job. Reporting a defect means describing something you were just looking at, and a modal is the one kind of window that forbids going back to look at it: it blocks the page underneath, only one can exist, and closing it to check a detail throws the draft away. As a tool window it minimizes to a hexhead, several can be open at once, and the member can work the product with the form still sitting there. 460x690, resizable, and the tool chrome's own colours — the same footprint as chat, support and the email composer, which is what lets the dock pack it into the row instead of cascading around it. It can now be raised with nothing but a bug id, so it fetches the report itself rather than being handed a DTO: window props survive a page reload, a DTO would not. The nav's "Report System Bug" opens it in place instead of navigating to /support — the old route walked you away from the thing you were about to describe. ?report=1 still works for anyone who bookmarked it. Because there may be no list mounted at all, saving announces on a system-bugs:changed event rather than calling back to a parent; any list on screen reloads itself. FILTERS OPEN ON THE QUEUE. Status starts on New — the reports somebody came to work through — with every type and every site. And all three dropdowns now pass displayEmpty: MUI skips the display pass entirely for a falsy value, so "All types" / "All statuses" / "All sites" were selectable and then drew as a blank box. A filter that looks unset while it is filtering is the one state a filter bar must never be in. SENT_TO_AI IS GONE, folded into In Progress: handing a bug to an AI session is somebody working on it, and a second status for the same fact split the triage list in two for no gain. WONT_FIX now reads "Won't Fix / Denied" — it is the terminal state for an enhancement nobody will build as well as a bug nobody will fix. That label's English changed, so its dictionary key is renamed; keys are now a spelled-out map rather than an interpolated template, so the one crooked key is visible instead of hidden inside a string. SITES ARE OPTIONAL. A member who is not sure which of five surfaces they were on is better off saying nothing than guessing.
· kamo-shared-library
Practice scheduling, waitlist and recall (SP6)
Availability stays as RULES. ScheduleBlock describes a provider's day and slots are computed for the window being viewed; pre-materialising five-minute slots for forty providers across a year is tens of millions of rows that go stale the moment someone changes their Tuesday. Only booked time is a row. The block's times are wall-clock LocalTime with a named IANA zone, because "clinic starts at 8am" has to survive daylight saving — a practice that stored instants finds its whole Monday an hour off twice a year. Overbooking is modelled as slotCapacity rather than left as a check to defeat, because clinics deliberately double-book against expected no-shows and an unmodelled policy just moves the workaround somewhere unauditable. Visit types are practice-configured, not an enum. Hard-coding them is the most common reason a scheduling module cannot be sold to a second specialty: dermatology books Mohs stages, OB books anatomy scans, each with its own duration, room requirement and prep instructions. patientBookable defaults FALSE — a new type appearing on the public booking page the moment it is created is how a stranger gets booked into a procedure slot. The duration is COPIED onto the appointment at booking, so editing the policy never silently moves appointments already on the book. Appointment keeps cancelledBy separate from status because a no-show, a patient cancellation and a practice cancellation carry completely different money and different obligations — a no-show may be billable and counts against the patient's standing, a practice cancellation creates a duty to rebook. Eligibility is verified per VISIT, not per patient: coverage lapses between visits, and a stale check is how a practice discovers at billing time that it saw an uninsured patient for free. The waitlist exists because a cancellation twelve hours out is almost never filled by a human calling down a list, and an empty slot is unrecoverable revenue and an unseen patient at the same time. Offers expire: one unanswered offer blocking a slot while nobody else hears about it is what makes most waitlists worse than useless. Offers respect the patient's stated constraints, because offering a Tuesday to someone who said Mondays trains them to ignore the messages and costs the practice the whole list. PatientRecall is the last piece and the one most systems skip. "Come back in six months" is the most-lost instruction in medicine — said at the end of a visit, written in a note nobody re-reads, then left to the patient to remember. It is deliberately NOT a far-future appointment: that is inventory the practice cannot use and the patient will not keep. It is an obligation with a due date, an outreach window that starts late enough not to train people to ignore it, a counted outreach history, and a closure reason that stops "no longer due" from quietly meaning "we gave up". 1781 tests green.
· MediaService
Naming a site is optional
It was required, and requiring it was wrong. A site is a hint for whoever picks the report up — a member who is not certain which of five surfaces they were looking at is better off saying nothing than guessing, because a wrong site sends the work somewhere it does not belong while no site sends it nowhere in particular, which is where it already was. An unrecognised name is still refused. That is a different rule: "I did not answer" comes from a member, "MARKETNIG" comes from a client this server does not agree with, and swallowing it would file a report whose site silently vanished. parseSites and parseSite are static now so the boundary can be exercised without a Spring context, matching visibilityScope and likePattern above them.
· InitializerService
Move status off the ordinal and onto the name
Adds STATUS_NAME / STATUS_APPLIED_NAME, fills them from the ordinals, then drops the ordinal columns — in that order, which is the only order that keeps the data. SENT_TO_AI (ordinal 3) maps to IN_PROGRESS: it is retired, and a bug handed to an AI session is a bug somebody is working on. Left unmapped it would fall to the ELSE and every dispatched bug would read as untriaged again. The backfill is guarded on the OLD column still existing rather than on a marker table — the next statement drops it, so it runs exactly once per database and is a silent no-op forever after instead of an ERROR in the log on every boot. Its UPDATEs go through EXECUTE so PL/pgSQL never plans a reference to a column that may already be gone. The ordinal columns are dropped rather than left behind. system_bug.status was NOT NULL DEFAULT 0, so every row written after the switch would read as NEW there while the real status said something else — a second, stale answer that anything can still read is worse than no column at all. Already applied by hand to yb-tserver:5433 (this service has no CI, so pushing it ships nothing); both existing rows moved from SENT_TO_AI to IN_PROGRESS.
· kamo-shared-library
Results, doses, procedures and the real medication list (SP7)
DiagnosticReport keeps the report AND its Observations, because results arrive as a report and are acted on as individual values and both views have to be true at once. A CBC is one report with fourteen Observations: the haemoglobin trend needs the fourteen, the PDF the patient reads needs the one. abnormalFlag and criticalFlag are separate because the obligations are — abnormal is reviewed, critical is ACTED ON, and conflating them buries the second in the first. releasedToPatientAt is separate from issuedAt because Information Blocking makes release the default and the timing provable; withholding needs a recorded reason, so "not released yet" can never quietly mean the practice prefers to call first. Immunization treats a refusal as a RECORD, not an absence. not-done with a reason is what distinguishes a patient who decoderd from a patient nobody asked, and only the second is a care gap. CVX is the key because that is what state registries speak, but the lot number is indexed on its own: a recall is issued against a LOT, and "who did we give this lot to" is a phone-calls-that-afternoon question. The VIS edition date is statutory under the NCVIA and there is no reconstructing it later. Procedure keeps the SNOMED code as clinical truth and carries CPT beside it as what was CODED. Deriving the chart from the claim produces a chart describing what was reimbursable rather than what happened, and loses every non-billable procedure entirely. notBilled with a reason makes a deliberate non-charge distinguishable from an oversight — the difference between a policy and the single largest recoverable loss in most practices, which is invisible precisely because a missing charge row raises nothing. MedicationStatement is the list of what the patient is ACTUALLY taking, and it is not the prescription list. A prescription says what a clinician intended; a statement says what the patient reports doing, including other practices' drugs, OTCs, interacting supplements, and the drug they stopped three weeks ago without telling anyone. Reconciliation is the act of comparing the two and cannot happen if they are one table. adherence carries the finding that changes management: an unresponsive blood pressure means something entirely different when the drug is not being taken. And taking-not-as-directed still counts as taking, because half a dose is still a drug in the body. DiagnosticReport dropped its encounter index rather than take a fourth distributed write per result — the chart loads by patient and filters. 1757 tests green.

உங்கள் உலகம் காத்திருக்கிறது.
நிமிடங்களில் இலவசமாகத் தொடங்கவும். நீங்கள் தயாராக இருக்கும் போது பொருட்களை சேர்க்கவும் - எந்த மாற்றமும் இல்லை, புதிய உள்நுழைவுகள் இல்லை.
எப்பொழுதும் இலவசம் — கடன் அட்டை தேவையில்லை