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.
| Action | Required input |
|---|---|
| Receive | case_id, case_version, bed_id, bed_version, reason |
| Move | Receive fields plus stay_id, stay_version, source_bed_version |
| Accept handoff | case_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/ciOnly 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.
| Requirement | Evidence |
|---|---|
| R1/R3 | Receive races admission create/assign/transfer; one owner and no partial source release |
| R3/R5 | No-admission receipt, exact nullable identity and same-stay move with source cleaning |
| R6/R7 | Missing gate/policy blocks clearance; verified synthetic handoff retains obligations; no signed-discharge action |
| R8 | Real grants, fresh assignment and membership, enforced-role isolation, audit rollback |
| R11 | Exact replay, shared cross-action key conflict, stale case/stay/bed versions and unknown outcomes |
Changed Paths
Under backend/domains/clinical/:
policies/hospital/recovery_policy.rbservices/hospital/recovery/capacity_lock.rbservices/hospital/recovery/handoff_authority.rbservices/hospital/recovery/receipt.rbservices/hospital/recovery/stay_command.rbservices/hospital/recovery/stay_input.rbservices/hospital/recovery/stay_projection.rbservices/ipd/admit_service.rbservices/discharges/create_service.rbservices/hospital/surgical_cases/transition_service.rbtest/policies/hospital/recovery_policy_test.rbtest/policies/clinical_policy_matrix_test.rbtest/services/hospital/recovery_test.rbtest/services/hospital/recovery_concurrency_test.rbtest/services/hospital/recovery_capacity_lock_test.rbtest/services/hospital/surgical_cases_test.rbtest/services/discharges/create_service_orders_test.rb
Other paths:
backend/test/support/hospital_recovery_helpers.rbbackend/test/support/hospital_recovery_cleanup_helpers.rbbackend/db/migrate/20261020190001_add_hospital_recovery_permission.rbbackend/db/migrate/20261021010001_fix_recovery_source_encounter_identity.rbbackend/db/migrate/20261021010002_index_recovery_source_encounters.rbbackend/db/seeds/reference/015_seed_permissions.rb- Intended Recovery objects and version entries in
backend/db/structure.sql. backend/domains/reference/models/permission.rband 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.