Kulcare Docs
DeveloperHospital surgery center

Hospital MAR U6 Typed Transport

Live MAR adapter, runtime validation, scoped cache keys, private retry recovery, and manual QA.

Scope

This is local U6 of the canonical MAR plan. It is not programme U6 completion. Source checkpoint: 41d24a3fea. The same integration branch and draft PR 2757 carry the work.

Clinical owns orders, versions, outcomes and receipts. Hospital owns the typed consumer and case context. Identity owns effective grants. The comparable frontend implementation is hospitalLabApi.ts. MAR uses the same shared transport, runtime validation and structured errors. It has its own recovery record because clinical payloads must not be stored in the browser. No backend code, migration, permission default, dependency or clinical policy changes belong to this unit.

Implementation runs in the main agent context. No worker or served-model receipt is claimed. The two local review servers remain separate from these changes.

Consumer Contract

services/hospitalMarApi.ts is a live-only adapter. It exposes:

MethodOperation
capabilityCurrent source, selected policy and read/write availability
listFiltered occurrences, PRN availability, setup gaps, counts and pagination
detailOne occurrence bound to its canonical request
sources, source, versionsCase-scoped eligible sources, current instructions and version history
orderCreate, enroll, revise, hold, resume or stop a source
doseAdminister, hold, resume, omit, refuse or confirm a missed occurrence
prnRecord a PRN outcome with no fabricated occurrence selector
correctCorrect approved outcome fields with the current command chain
historyPaginated immutable occurrence receipts
receiptExact operation-key lookup; authorized absence is null

Every method accepts an abort signal. UUID selectors are encoded. The adapter maps snake-case fields to camel-case fields. Decimal doses stay strings. FHIR medication objects and source snapshots keep their source-owned field names. Strict action schemas reject extra actor, patient or clinical command fields. Response schemas reject unknown kinds, actions, invalid dates, missing fields, invalid units and versions. A successful HTTP status alone is not success. Occurrence/request selectors and mutation receipt keys must match the request. Mutation receipt actions and current-result shapes must also match the operation. Errors keep the HTTP status, code and request ID through withApiErrors. The adapter never retries a write, calls Recovery Board, or falls back to mocks.

hospitalQueryKeys has a separate canonical-mar family. List filters, source selectors, occurrence IDs, page numbers and receipt keys sit below tenant, facility and profile scope. Existing prototype keys and methods remain separate. Use the family prefix for MAR invalidation. The live UI must also invalidate the affected case, canonical order readers and source-backed dashboard metrics.

The shared resource-permission hook exposes read/source/prescribe/record/manage UX checks. Permission loading and failures deny MAR access. Read-only tenant access removes write controls. Effective grant checks do not establish clinical assignment, approved policy or row eligibility; use server actions as well.

Lost-Response Recovery

services/marRetryStore.ts stores one pending intent per tenant/facility/profile in session storage. It stores the scope, command kind, opaque case/request/ occurrence selectors, original operation UUID and SHA-256 payload digest. It also stores the original concurrency envelope, field names/types for exact re-entry, and a known-rejection marker when applicable. The envelope includes versions, policy hash, action, entry mode and acknowledgement/target IDs. It stores no raw command, name, dose, medication, indication or reason. Object-key ordering does not change the digest. Decimal strings, dates and clinical text are not silently changed.

  1. Save the intent before sending the command. Storage failure blocks submission.
  2. Keep private drafts in memory only. A changed scope must cancel its requests and clear private component state. It must not delete an unresolved old key.
  3. After a lost response or reload, reconcile with reconcileMarRetry first.
  4. confirmed means a receipt exists. Refetch current data before clearing the recovery record with its original key. A receipt is not current readiness.
  5. absent means an authorized lookup found no receipt. Keep the original key. Require re-entry and verifyMarRetryPayload before a same-payload retry. Use the separate original-command form even if the current action disappeared. Submit the saved preconditions, not a later source, case or occurrence version.
  6. unresolved covers denied, failed, malformed or aborted lookups. Keep the key and do not submit a replacement operation.
  7. A direct known HTTP 400/422 rejection or local INVALID_INPUT can release the key. A known HTTP 409 conflict requires a successful current-data refresh. Save the rejection before that refresh. If the refresh fails, reload must retain the rejection and retry the reads, not convert it to an absent command. Never use a later lookup error as evidence that an earlier command failed.

Malformed stored data fails closed. A key or scope mismatch cannot clear another intent. Recovery does not persist a retry authorization across reloads. Older recovery records without an original envelope remain readable for receipt lookup. Do not invent missing preconditions for an absent legacy record. Keep the key and use supervised recovery. Never put clinical drafts in storage to make recovery easier.

Verification

Proof-first tests failed because the new adapter and store did not exist. A further regression failed because a wrong-action receipt was accepted; the adapter now binds receipt actions and result shapes. Tests use synthetic data. The same-object-key and local-validation regressions failed before their fixes. The actual shared transport is Axios, not the separate fetch helper. An adapter- level network fixture verifies status, code, details and request ID through the real Axios interceptors. No shared transport change was needed.

  • Focused adapter/recovery/transport/permission checks pass: 64 tests. They cover T29-T32 and include the direct transport boundary.
  • Hospital strict typecheck and ESLint pass with no errors.
  • Hospital production build passes. The existing large-chunk warning remains.
  • The final full Hospital suite passes: 34 files, 296 tests. The final run uses pnpm --filter @kulcare/hospital test:run --silent to suppress existing noisy component warnings; it does not skip tests.
  • Canonical pnpm run ci passes: zero lint errors, zero test failures, and wire guards pass. The workspace has 212 existing type errors against a 254 ceiling. No baseline was raised. A green ratchet is not a clean workspace typecheck.
  • Existing component tests emit React act warnings. They are not evidence of browser or human nursing acceptance.

Review

CodeRabbit CLI 0.9.0 reviewed a 14-file isolated snapshot based on 41d24a3fea. It found two retry defects: explicit undefined values blocked payload hashing, and a locally rejected input could leave a pending intent that could not clear. Both have failing-before-fix regressions and corrections. Undefined object keys now follow JSON omission rules; absent/null optional selectors have one digest. Local INVALID_INPUT can release the original key because no POST was sent. Denied, failed and uncertain outcomes still retain it.

The seven-file follow-up completed with zero findings. It includes the new transport regression and the reviewed corrections. A final integration scan found that useHospitalQueryScope returns an opaque composite profile key, not a UUID. A failing-before-fix regression now proves that actual key format and profile-switch isolation. The recovery schema accepts that bounded opaque key; tenant and facility selectors remain UUIDs. The final four-file review covers that correction and the added instruction/late-entry tests. It completed with zero findings. Only documentation status and evidence text changed after that reviewed snapshot. No credentials, local configuration, schema dump, real patient data or session artifacts enter either review snapshot.

Code review: skipped (ce-code-review unavailable). This host does not expose the native subagent dispatch needed for that skill's full review and finish leaves. The CodeRabbit loop and a local manual diff scan are separate evidence, not a completed Compound multi-reviewer receipt. No extra model workers were started.

Manual QA

The visible live worklist is local U7, not this unit. Use these steps when that consumer is connected. Automated adapter tests do not prove browser acceptance.

  • Use authenticated MFA with mock mode off. Inspect capability and worklist requests. Confirm the selected facility appears in each route.
  • Confirm decimal 1.000 stays a string and offset times remain explicit.
  • Select an unapproved policy. Confirm unavailable writes, not mock success.
  • Inspect session storage after a synthetic interrupted command. Confirm it contains only opaque selectors, scope, operation key and digest.
  • Reload after a lost response. Confirm exact-key lookup occurs before retry.
  • Confirm a receipt causes a current-data refetch before recovery is cleared.
  • Confirm an authorized absent lookup retains the original key and requires matching re-entry. Changed dose, reason or selectors must not match the digest.
  • Deny or interrupt receipt lookup. Confirm the unresolved key remains.
  • Switch tenant, facility and profile. Confirm no prior private draft, cached row or mutation can appear or submit in the new scope.
  • Confirm view-only, loading, failed-permission and read-only states do not expose nursing writes. Backend authorization must still reject direct calls.

Open Release Gates

The /hsp/mar live page is not connected by this unit. No clinical policy is approved or enabled. No deployment, browser nursing QA, clinical acceptance or pilot sign-off is claimed. Local U7 connects the worklist and case handoffs. Local U8 proves the real-HTTP journey and records the remaining approvals.

On this page