Kulcare Docs
DeveloperHospital surgery center

Recovery U4 Execution

Stay commands, shared occupancy locks, handoff boundaries, verification and manual QA.

Scope And State

Source commit: 344a14f8b08c1e77699e94ec34d7e92b23ffce4c. This is local U4 of the Recovery plan, not programme U4 or signed-discharge delivery. Continue draft PR 2757.

Stay services and shared-writer protections are implemented locally. The parent has reproduced focused tests. Simplification, CodeRabbit and the compound review have completed. The confirmed findings are fixed and final backend verification passes. The parent owns the canonical U4 commit and push. There is no new Recovery HTTP route or live UI in this unit.

Clinical owns this code. Current references are Recovery::ResourceCommand, canonical capacity and admission services, the MAR case-first lock order, fresh assignment policies, immutable Recovery history, and Audit::Dispatch. There is no second occupancy ledger or medication source.

Interfaces

Recovery::StayCommand.call(facility:, actor:, operation_key:, action:, input:) returns the established Result(command:, error_code:, replayed:, outcome:). Actions are receive, move and accept_handoff. Server context supplies the authenticated actor, tenant and selected facility.

ActionRequired input
Receivecase_id, case_version, bed_id, bed_version, reason
MoveReceive fields plus stay_id, stay_version, source_bed_version
Accept handoffcase_id, case_version, stay_id, stay_version, source_bed_version, admission_id, admission_version, reason

Operation and record keys are UUIDs. Case/stay/bed versions are nonnegative integers inside these services. Admission versions are opaque UTC timestamps. Unknown fields and coerced versions fail validation. No client field supplies patient identity, actor, tenant, clinical approval or discharge proof.

Receive creates one stay for a case already in recovery. It preserves the exact nullable encounter and existing MAR links. It creates no admission or encounter. A move changes the current bed of that same stay. The source enters cleaning. Each success appends a movement with the previous event, one immutable command receipt and required transactional audit. Audit failure rolls back all effects.

Recovery::CapacityLock.call(facility:, bed_ids:) discovers the canonical parent chain, locks all sorted groups and rooms before all sorted beds, then checks that the chain did not change. Supported admission writers use this same lock helper.

Recovery::Receipt.call(facility:, actor:, operation_key:) checks current stay authority before lookup. It distinguishes a confirmed receipt, authorized absence, forbidden access and an unknown lookup failure. U6 must add action/source-specific HTTP authorization and protected serialization before exposing receipts.

Recovery::StayProjection.call(facility:, actor:) returns current stays and cases waiting for their first stay. It does not create a waiting stay or apply a date filter that hides overnight occupancy. U6 owns bounded HTTP search/pagination, source redaction and protected read audit.

Authority And Replay

RecoveryPolicy requires current active membership, user and facility assignment, selected tenant/facility, view_hospital and view_surgical_cases. Commands also require an active practice and the real manage_recovery key. Receipt reads retain current authority checks for suspended-practice reconciliation. Owner/admin personas alone grant no clinical assignment.

The catalog prerequisite moved forward from U6 because the role-check guard requires a seeded key. Migration 20261020190001 and the reference seed add manage_recovery to hospital_operations at manage, with empty default-role grants. Access/default/snapshot/key-behavior tables and the policy matrix cover it. The migration rejects an existing row with different scope or defaults. It does not replace existing grants. Rollback preserves the row and requires a reviewed forward migration. No production nursing assignment or default grant is added.

The permission model accepts an empty role array, but rejects null, a non-array value and unknown roles. Tests first reproduced both validation defects. The Admin role keeps its previous keys and now reads as Custom in this area. This is the expected result of withholding the new clinical key, not a default grant. U6 still owns action/source-specific HTTP authorization and protected reads.

The shared facility scheduling lock precedes case/capacity work. Resource and stay commands use the same facility/key advisory namespace. Authorization repeats after that lock and before receipt replay. An exact replay precedes stale-version checks. A reused key with changed action, selector or payload conflicts.

Disabled app/module availability blocks new receive. Authorized active-care moves, reconciliation and receipt recovery remain possible. It does not grant authority.

Continued Care And Discharge

Handoff accepts only a supported active same-patient, same-facility admission with an active receiving encounter. It cannot target the closing stay's own encounter. The authenticated accepting actor is the receiving acknowledgement; client actor and acknowledgement fields are rejected. Receiving authority also needs manage_ipd.

The immutable result preserves the receiving context and references to the original case medication links, current source versions and open dose occurrences. It does not copy medication values, resolve obligations, finish the original encounter, complete the case or create patient discharge. The source bed enters cleaning once.

The production handoff policy loader deliberately returns unavailable in U4. U5 must connect approved policy and current evidence. Positive U4 tests supply test-only authority; they are not evidence of a deployed approved policy. No eligible or approved receiving workflow means HANDOFF_UNAVAILABLE, not discharge.

Generic case ready/completed transitions fail closed until the current Recovery gate is connected. Existing READINESS_BLOCKED responses retain the source blockers and add the RECOVERY_GATE_UNAVAILABLE reason. Active care blocks direct cancellation/completion through the U2 source constraints. Browser-supplied signed-discharge proof is invalid input. Programme U8 owns verified signed-discharge release. U4 adds no callable shortcut.

Shared Writer Repairs

Admission create, assignment and transfer use transaction savepoints and the canonical parent-before-bed lock helper. Assignment and transfer reload/lock the admission before checking its active state or releasing its current bed. The new admission is established bedless inside the same transaction before it claims capacity. A losing create rolls back its encounter and episode, including when an outer RLS transaction exists.

Discharge locks related cases, the current admission and active stays before capacity. It validates the reloaded admission, rejects active Recovery care on the same encounter, and uses the locked current bed. A cached object cannot discharge the admission twice. Ordinary valid admission transfer/discharge still works.

Receive checks the current non-null source encounter after the case lock. A terminal encounter cannot acquire active Recovery care. Null encounters remain supported. Handoff resolves the canonical encounter's patient through its direct ID, or its registration only when the direct ID is null. A non-null mismatch never falls back. Accepted uppercase bed selectors use canonical comparison; the request fingerprint still distinguishes changed payloads.

Migration 20261021010001 repairs the Recovery source-encounter foreign key. Canonical admissions can store patient identity through their registration. The scoped tenant/facility/encounter FK and a new trigger enforce the exact registration and resolved patient. The trigger takes a source row share lock and rejects terminal care. Another guard protects linked source identity and rejects terminal status while Recovery care is active. This covers generic source writes as well as the command. No patient or encounter is fabricated. Migration 20261021010002 indexes source-encounter lookups for these guards. The schema diff includes only those objects and the three migration versions.

The source repair uses five-second lock and sixty-second statement limits. Its preflight rejects conflicting existing care. It does not rewrite patient data. Run it with the normal schema-owner migration role; insufficient forced-RLS visibility must fail. Preserve the source repair on rollback: valid care can depend on the nullable canonical link. Use reviewed forward recovery instead. The separate lookup index can be removed without deleting clinical records.

Proof

The implementation worker reported proof-first failures for absent StayCommand, the generic ready transition, duplicate discharge through a cached admission, orphan source care after a losing admission inside an outer transaction, and discharge of an encounter with active Recovery care. These are worker observations; the parent did not independently witness their pre-fix runs.

Initial parent checkpoint: 105 tests, 2,769 assertions, no failures/errors, one existing matrix skip, seed 50494. Seven independent-connection races run under kulcare_rls_test. The parent added distinct backend-PID assertions. Race outcomes can change assertion totals; the seed above identifies this run.

After the catalog pull-forward, an expanded run passed 443 tests and 3,937 assertions, with no failures/errors and one existing matrix skip, seed 33246. That checkpoint predates the review corrections and source-identity repair. It includes the readiness/Lab regressions, permission model and complete RBAC directory, not only the original seven Clinical files below.

Parent review reproduction: 21 tests, 173 assertions, three failures and one error. This exposed terminal-source intake, canonical handoff identity and the uppercase selector crash before their fixes. The next focused four-file run passed 75 tests, 874 assertions, with no failures/errors/skips.

Final focused set: 583 tests, 5,344 assertions, no failures/errors and one existing policy-matrix skip, seed 25339, on each schema path. The migration database applies the forward repairs; the SQL database loads the intended dump. Both check actual source FK/triggers/index, enforced-role tenant isolation, permission tables, readiness/Lab regressions and canonical writer behavior. The first SQL-only attempt lacked the test-role grants after CI rebuilt the database. It is not valid RLS evidence. Provisioning the documented grants and repeating the full set produced the passing result above.

The source-guard proof includes both commit orders against generic terminal status and patient-identity updates. Another test rejects conflicting existing care before DDL, and an insufficient-RLS role cannot accept an empty preflight. The dedicated proof run passed 20 tests, 181 assertions, with no failures/errors/skips, seed 64289.

One full CI attempt reported 15 failures and one error outside Hospital across Patient PDF/rate-limit, appointment-order and PHR tests. All 80 tests in those three files then passed in isolation on the corrected SQL schema. The full same-seed rerun passed 18,926 domain tests, 88,188 assertions, no failures/errors and eight existing skips. Pricing passed 20 tests and 26 assertions, with no failures/errors/skips. Seed: 25339. The earlier failures did not recur; their precise environmental cause is not established. No CI baseline or unrelated application code changed to hide those results.

Canonical command, from backend/, using the owned disposable database:

DATABASE_URL=postgresql:///kulcare_hospital_recovery_u2_sql_20261020_test RAILS_ENV=test CI_DOMAINS_SHARDS=4 CI_BASE_REF=origin/docs/new-schema-design SEED=25339 bin/ci

Only run this command after confirming that its disposable database and fixed shards are not in use. It rebuilds those test schemas. The Fumadocs build passes and indexes 512 pages. Its existing unrelated Reports HTML warning remains. No frontend runtime changes or browser acceptance are claimed for this unit.

From backend/, with the configured Ruby/PostgreSQL toolchain:

RAILS_ENV=test PARALLEL_WORKERS=1 DATABASE_URL=postgresql:///kulcare_hospital_recovery_u4_20261020_test bin/rails runner 'ActiveRecord.maintain_test_schema=false; abort("Unexpected database") unless ActiveRecord::Base.connection.select_value("SELECT current_database()") == "kulcare_hospital_recovery_u4_20261020_test"; $LOAD_PATH.unshift(Rails.root.join("test").to_s); %w[domains/clinical/test/services/hospital/recovery_test domains/clinical/test/services/hospital/recovery_concurrency_test domains/clinical/test/services/hospital/recovery_capacity_lock_test domains/clinical/test/policies/hospital/recovery_policy_test domains/clinical/test/policies/clinical_policy_matrix_test domains/clinical/test/services/hospital/surgical_cases_test domains/clinical/test/services/discharges/create_service_orders_test].each { |path| require Rails.root.join(path) }'

The owned U4 database is a clone of the verified migration-built U3 database. Focused runs disable schema maintenance and never reset shared data. Fixtures are synthetic. Nontransactional cleanup is tenant-scoped, clears canonical links in FK-safe order and restores temporary trigger state in its transaction. Synthetic discharge summaries and protected-access logs are removed before their synthetic care graph. This helper must never run against shared or patient databases. The two race-coordination methods have local complexity directives, matching the existing U3 helper. Raw-writer proof has a local validation-skip directive. No lint configuration or baseline is weakened. The owned Ruby files pass lint.

RequirementEvidence
R1/R3Receive races admission create/assign/transfer; one owner and no partial source release
R3/R5No-admission receipt, exact nullable identity and same-stay move with source cleaning
R6/R7Missing gate/policy blocks clearance; verified synthetic handoff retains obligations; no signed-discharge action
R8Real grants, fresh assignment and membership, enforced-role isolation, audit rollback
R11Exact replay, shared cross-action key conflict, stale case/stay/bed versions and unknown outcomes

Changed Paths

Under backend/domains/clinical/:

  • policies/hospital/recovery_policy.rb
  • services/hospital/recovery/capacity_lock.rb
  • services/hospital/recovery/handoff_authority.rb
  • services/hospital/recovery/receipt.rb
  • services/hospital/recovery/stay_command.rb
  • services/hospital/recovery/stay_input.rb
  • services/hospital/recovery/stay_projection.rb
  • services/ipd/admit_service.rb
  • services/discharges/create_service.rb
  • services/hospital/surgical_cases/transition_service.rb
  • test/policies/hospital/recovery_policy_test.rb
  • test/policies/clinical_policy_matrix_test.rb
  • test/services/hospital/recovery_test.rb
  • test/services/hospital/recovery_concurrency_test.rb
  • test/services/hospital/recovery_capacity_lock_test.rb
  • test/services/hospital/surgical_cases_test.rb
  • test/services/discharges/create_service_orders_test.rb

Other paths:

  • backend/test/support/hospital_recovery_helpers.rb
  • backend/test/support/hospital_recovery_cleanup_helpers.rb
  • backend/db/migrate/20261020190001_add_hospital_recovery_permission.rb
  • backend/db/migrate/20261021010001_fix_recovery_source_encounter_identity.rb
  • backend/db/migrate/20261021010002_index_recovery_source_encounters.rb
  • backend/db/seeds/reference/015_seed_permissions.rb
  • Intended Recovery objects and version entries in backend/db/structure.sql.
  • backend/domains/reference/models/permission.rb and its new model test.
  • Identity RBAC access/default/key-behavior tables, snapshot and permission migration test.
  • Identity access-level and historical migration catalog tests.
  • backend/domains/clinical/test/models/hospital/recovery_source_encounter_test.rb
  • This execution record.

Review Corrections

The compound review completed as 20261020-185302-e8a9a294, with one P1 and three P2 findings. The fresh validator retry confirmed all four after the first attempt stopped at the usage limit. The parent applies the corrections; the receipt is review evidence, not proof that the fixes or release passed.

  • Reject receive after source discharge. Prove both ordered writers and direct source guards.
  • Correct the catalog/migration handoff in this guide.
  • Accept canonical registration-based handoff identity without accepting a non-null mismatch.
  • Handle uppercase bed selectors and retain exact-payload replay/conflict meaning.

The review also requests exact handoff source/revision assertions, real two-tenant isolation and absent-versus-unavailable receipt tests. These are added to the focused regression scope. No clinical policy or threshold is invented.

CodeRabbit completed the first 29-file scoped review. Its safe capacity-error message and documentation corrections are accepted. Its proposed guarded permission deletion is rejected: the active backend instructions explicitly permit forward-only recovery where rollback can remove grants. A tenant grant check alone also misses member overrides. Retain the catalog row on rollback. The scoped source follow-up completed across 34 files. It reported two claims that the parent rejected with source evidence: the linked plan is tracked at the stated path, and protect_recovery_case already forbids detaching an active stay from its case encounter. The latter also has a raw-writer rejection test in models/hospital/recovery_test.rb. No lookup widening is required. The extra source-race/preflight tests and final evidence text were added after that snapshot; they have parent proof, not an exact final CodeRabbit receipt.

Two fresh bounded follow-ups checked source serialization/security and migration safety/parity with no new code findings. Their requested extra source races and preflight tests are now included in the final focused proof. These reviews do not replace whole-PR acceptance or deployed policy approval.

Simplification reused FacilityBed#release! for admission transfer, kept lock queries as ID projections and removed a redundant policy-loader comment. It did not rewrite proof tests. The local adversarial review ran; the separate peer launcher requires forbidden npx and did not run. No independent-provider or served-model receipt is claimed.

Manual QA And Release

HTTP/browser steps require the later U6/U7 surfaces. Do not test mock controls as proof of live persistence. Record candidate revision and synthetic facility first.

  • Receive a no-admission case, reload and confirm the exact case/MAR context.
  • Move the same stay and confirm the source becomes cleaning with full history.
  • Race two clients, including an admission writer. Confirm one committed owner.
  • Repeat a committed operation key. Confirm one event, receipt and audit.
  • Reject stale versions and foreign/inactive/occupied capacity without partial changes.
  • Reject invalid, self, foreign, inactive or unapproved handoff targets.
  • Under an approved test policy, accept into a supported receiving context and confirm open obligations remain visible. Otherwise record unavailable, not passed.
  • Confirm generic completion/cancellation or client discharge proof cannot erase active care.
  • Recheck ordinary admission assignment, transfer and discharge.
  • Record deployment, clinical policy, D4/D5 and pilot decisions separately.

No API/UI, signed-discharge workflow, production policy or default permission grant is delivered by U4. The three migrations above are required deployment dependencies. No deployment or human approval ran.

Execution Receipt

Native implementation worker: 01a15958-18b2-7060-bc20-8f65694e0923. Requested profile: hospital_implementer, configured gpt-6.1-sol / high. The parent collected its checkpoint, stopped worker authoring, inspected the real tree and took over verification. No other writer ran at the same time. Served-model, effort, billing/token usage and cost receipts are unavailable. The canonical branch, commits, PR, full checks and reviews remain parent-owned.

On this page