diff --git a/.appledeploy.example b/.appledeploy.example index 84f36cd7..fb71e967 100644 --- a/.appledeploy.example +++ b/.appledeploy.example @@ -40,7 +40,7 @@ HUMMINGBIRD_SCHEME=Hummingbird NIGHTINGALE_NAME="Zephyrus-Nightingale" NIGHTINGALE_APP_ID= NIGHTINGALE_BUNDLE_ID=net.acumenus.nightingale -NIGHTINGALE_DIR=hummingbird/iosPatientApp +NIGHTINGALE_DIR=nightingale/iosApp NIGHTINGALE_XCODEPROJ=Nightingale.xcodeproj NIGHTINGALE_SCHEME=Nightingale diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b278a57f..40149227 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -132,7 +132,7 @@ jobs: env: APP_ENV: testing APP_KEY: base64:MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDA= - NIGHTINGALE_CONTEXT_KEY: test-hummingbird-patient-context-key-v1 + HUMMINGBIRD_PATIENT_CONTEXT_KEY: test-hummingbird-patient-context-key-v1 APP_URL: http://zephyrus.test DB_CONNECTION: pgsql DB_HOST: 127.0.0.1 @@ -260,7 +260,7 @@ jobs: env: APP_ENV: testing APP_KEY: base64:MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDA= - NIGHTINGALE_CONTEXT_KEY: test-hummingbird-patient-context-key-v1 + HUMMINGBIRD_PATIENT_CONTEXT_KEY: test-hummingbird-patient-context-key-v1 APP_URL: http://zephyrus.test DB_CONNECTION: pgsql DB_HOST: 127.0.0.1 @@ -581,7 +581,7 @@ jobs: env: APP_ENV: testing APP_KEY: base64:MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDA= - NIGHTINGALE_CONTEXT_KEY: test-hummingbird-patient-context-key-v1 + HUMMINGBIRD_PATIENT_CONTEXT_KEY: test-hummingbird-patient-context-key-v1 APP_URL: http://127.0.0.1:8084 DB_CONNECTION: pgsql DB_HOST: 127.0.0.1 @@ -697,7 +697,7 @@ jobs: env: APP_ENV: testing APP_KEY: base64:MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDA= - NIGHTINGALE_CONTEXT_KEY: test-hummingbird-patient-context-key-v1 + HUMMINGBIRD_PATIENT_CONTEXT_KEY: test-hummingbird-patient-context-key-v1 APP_URL: http://127.0.0.1:8085 DB_CONNECTION: pgsql DB_HOST: 127.0.0.1 @@ -782,6 +782,271 @@ jobs: if-no-files-found: error retention-days: 30 + mobile-brand-assets: + name: Mobile product brand assets + runs-on: macos-15 + needs: changes + if: needs.changes.outputs.reuse != 'true' && needs.changes.outputs.docs_only != 'true' + timeout-minutes: 10 + + steps: + - uses: actions/checkout@v4 + + - name: Verify Hummingbird and Nightingale icon derivations + run: | + bash scripts/ci/verify-mobile-brand-assets.sh . + bash scripts/ci/verify-mobile-brand-surfaces.sh . + + nightingale-contract-foundation: + name: Nightingale contract foundation + runs-on: ubuntu-24.04 + needs: changes + # This remains active for docs-only changes because the contract artifact is + # intentionally stored under docs/nightingale/api-contract. + if: needs.changes.outputs.reuse != 'true' + timeout-minutes: 5 + + steps: + - uses: actions/checkout@v4 + + - name: Setup Node 22 + uses: actions/setup-node@v4 + with: + node-version: 22 + + - name: Setup PHP 8.2 + uses: shivammathur/setup-php@v2 + with: + php-version: "8.2" + coverage: none + + - name: Verify empty contract, held candidates, rights/pilot holds, backend foundation, and native product boundary + run: | + node scripts/ci/verify-nightingale-contract-foundation.mjs . --self-test + node scripts/ci/verify-nightingale-encounter-access-candidate.mjs . --self-test + node scripts/ci/verify-nightingale-today-candidate.mjs . --self-test + node scripts/ci/verify-nightingale-patient-journey-reference-candidate.mjs . --self-test + node scripts/ci/verify-nightingale-identity-source-candidates.mjs . --self-test + node scripts/ci/verify-nightingale-source-classification.mjs . --self-test + node scripts/ci/verify-nightingale-communication-notification-classification.mjs . --self-test + node scripts/ci/verify-nightingale-journey-preference-release-classification.mjs . --self-test + node scripts/ci/verify-nightingale-threat-hazard-model.mjs . --self-test + node scripts/ci/verify-nightingale-foundation-dependency-inventory.mjs . --self-test + node scripts/ci/verify-nightingale-namespace-foundation.mjs . --self-test + node scripts/ci/verify-nightingale-controlled-pilot-manifest.mjs . --self-test + node scripts/ci/verify-nightingale-background-rights.mjs . --self-test + php scripts/ci/verify-nightingale-backend-foundation.php . --self-test + bash scripts/ci/verify-nightingale-product-boundary.sh . + + # --------------------------------------------------------------------------- + # Nightingale — independent offline native foundation + # --------------------------------------------------------------------------- + nightingale-android-foundation: + name: Nightingale foundation (Android) + runs-on: ubuntu-24.04 + needs: changes + if: needs.changes.outputs.reuse != 'true' && needs.changes.outputs.docs_only != 'true' + timeout-minutes: 30 + defaults: + run: + working-directory: nightingale/androidApp + + steps: + - uses: actions/checkout@v4 + + - name: Enable KVM acceleration + run: | + echo 'KERNEL=="kvm", GROUP="kvm", MODE="0666", OPTIONS+="static_node=kvm"' | sudo tee /etc/udev/rules.d/99-kvm4all.rules + sudo udevadm control --reload-rules + sudo udevadm trigger --name-match=kvm + + - name: Setup Java 17 + uses: actions/setup-java@v4 + with: + distribution: temurin + java-version: "17" + + - name: Setup Android SDK + uses: android-actions/setup-android@v3 + with: + cmdline-tools-version: "12266719" + packages: >- + platform-tools + platforms;android-35 + build-tools;35.0.0 + + - name: Setup Gradle cache + uses: gradle/actions/setup-gradle@v4 + with: + cache-read-only: ${{ github.ref != 'refs/heads/main' }} + validate-wrappers: true + + - name: Verify source, governance, and Gradle product boundaries + run: | + bash ../../scripts/ci/verify-nightingale-product-boundary.sh ../.. + node ../../scripts/ci/verify-nightingale-background-assets.mjs ../.. --self-test + node ../../scripts/ci/verify-nightingale-foundation-dependency-inventory.mjs ../.. --self-test + ./gradlew --no-daemon --stacktrace verifyNightingaleProductBoundary + + - name: Run Debug and Release unit tests + run: ./gradlew --no-daemon --stacktrace testDebugUnitTest testReleaseUnitTest + + - name: Run Debug and Release lint + run: ./gradlew --no-daemon --stacktrace lintDebug lintRelease + + - name: Assemble Debug and Release apps + run: ./gradlew --no-daemon --stacktrace assembleDebug assembleRelease + + - name: Verify Nightingale Release APK boundary + run: | + mapfile -t release_apks < <(find app/build/outputs/apk/release -maxdepth 1 -type f -name "*.apk" -print | sort) + [[ "${#release_apks[@]}" -eq 1 ]] || { + echo "Expected exactly one Nightingale Release APK; found ${#release_apks[@]}" >&2 + exit 1 + } + bash ../../scripts/ci/verify-nightingale-android-release-artifact.sh "${release_apks[0]}" + + - name: Run Debug instrumented tests on Android 35 + uses: reactivecircus/android-emulator-runner@v2 + with: + api-level: 35 + arch: x86_64 + target: google_apis + profile: pixel_6 + disable-animations: true + emulator-options: -no-window -gpu swiftshader_indirect -no-snapshot -noaudio -no-boot-anim -camera-back none + working-directory: nightingale/androidApp + script: ./gradlew --no-daemon --stacktrace connectedDebugAndroidTest + + - name: Upload Nightingale Android verification artifacts + if: always() + uses: actions/upload-artifact@v4 + with: + name: nightingale-android-foundation-verification + path: | + nightingale/androidApp/app/build/reports + nightingale/androidApp/app/build/test-results + nightingale/androidApp/app/build/outputs/androidTest-results + nightingale/androidApp/app/build/outputs/apk/debug/app-debug.apk + nightingale/androidApp/app/build/outputs/apk/release/*.apk + if-no-files-found: warn + retention-days: 14 + + nightingale-ios-foundation: + name: Nightingale foundation (iOS) + runs-on: macos-15 + needs: changes + if: needs.changes.outputs.reuse != 'true' && needs.changes.outputs.docs_only != 'true' + timeout-minutes: 40 + env: + IOS_NIGHTINGALE_ROOT: nightingale/iosApp + + steps: + - uses: actions/checkout@v4 + + - name: Select and begin booting an available iPhone Simulator + id: simulator + run: | + simulator_udid="$(bash scripts/ci/select-ios-simulator.sh)" + echo "udid=$simulator_udid" >> "$GITHUB_OUTPUT" + xcrun simctl boot "$simulator_udid" 2>/dev/null || true + + - name: Install pinned XcodeGen + env: + XCODEGEN_VERSION: 2.46.0 + XCODEGEN_SHA256: 4d9e34b62172d645eed6457cac13fc222569974098ef4ee9c3368bedf0196806 + run: | + curl --fail --show-error --silent --location \ + --output "$RUNNER_TEMP/xcodegen.zip" \ + "https://github.com/yonaskolb/XcodeGen/releases/download/${XCODEGEN_VERSION}/xcodegen.zip" + echo "${XCODEGEN_SHA256} $RUNNER_TEMP/xcodegen.zip" | shasum -a 256 --check + unzip -q "$RUNNER_TEMP/xcodegen.zip" -d "$RUNNER_TEMP" + echo "$RUNNER_TEMP/xcodegen/bin" >> "$GITHUB_PATH" + "$RUNNER_TEMP/xcodegen/bin/xcodegen" --version + + - name: Verify generated Xcode project and source boundary + run: | + bash scripts/ci/verify-nightingale-xcode-project.sh "$IOS_NIGHTINGALE_ROOT" + bash scripts/ci/verify-nightingale-product-boundary.sh . + xcodebuild -project "$IOS_NIGHTINGALE_ROOT/Nightingale.xcodeproj" -list + + - name: Build signed Debug app for testing + run: | + xcodebuild \ + -project "$IOS_NIGHTINGALE_ROOT/Nightingale.xcodeproj" \ + -scheme Nightingale \ + -configuration Debug \ + -sdk iphonesimulator \ + -destination "generic/platform=iOS Simulator" \ + -derivedDataPath "$RUNNER_TEMP/nightingale-ios-tests" \ + build-for-testing + + - name: Wait for simulator boot to complete + run: xcrun simctl bootstatus "${{ steps.simulator.outputs.udid }}" -b + + - name: Run XCTest unit suite + run: | + xcodebuild \ + -project "$IOS_NIGHTINGALE_ROOT/Nightingale.xcodeproj" \ + -scheme Nightingale \ + -configuration Debug \ + -destination "platform=iOS Simulator,id=${{ steps.simulator.outputs.udid }}" \ + -derivedDataPath "$RUNNER_TEMP/nightingale-ios-tests" \ + -resultBundlePath "$RUNNER_TEMP/NightingaleUnitTests.xcresult" \ + -only-testing:NightingaleTests \ + -parallel-testing-enabled NO \ + test-without-building + + - name: Run XCUITest reference journeys + env: + XCUITEST_RETRY_FLAGS: ${{ github.event_name == 'pull_request' && '-retry-tests-on-failure -test-iterations 3 -test-repetition-relaunch-enabled YES' || '' }} + run: | + xcodebuild \ + -project "$IOS_NIGHTINGALE_ROOT/Nightingale.xcodeproj" \ + -scheme Nightingale \ + -configuration Debug \ + -destination "platform=iOS Simulator,id=${{ steps.simulator.outputs.udid }}" \ + -derivedDataPath "$RUNNER_TEMP/nightingale-ios-tests" \ + -resultBundlePath "$RUNNER_TEMP/NightingaleUITests.xcresult" \ + -only-testing:NightingaleUITests \ + -parallel-testing-enabled NO \ + $XCUITEST_RETRY_FLAGS \ + test-without-building + + - name: Build unsigned Release app for artifact verification + run: | + xcodebuild \ + -project "$IOS_NIGHTINGALE_ROOT/Nightingale.xcodeproj" \ + -scheme Nightingale \ + -configuration Release \ + -sdk iphonesimulator \ + -destination "generic/platform=iOS Simulator" \ + -derivedDataPath "$RUNNER_TEMP/nightingale-ios-release" \ + CODE_SIGNING_ALLOWED=NO \ + CODE_SIGNING_REQUIRED=NO \ + build + + - name: Verify Nightingale Release app boundary + run: | + release_app="$RUNNER_TEMP/nightingale-ios-release/Build/Products/Release-iphonesimulator/Nightingale.app" + bash scripts/ci/verify-nightingale-ios-release-artifact.sh "$release_app" + + - name: Shut down test simulator + if: always() && steps.simulator.outputs.udid != '' + run: xcrun simctl shutdown "${{ steps.simulator.outputs.udid }}" 2>/dev/null || true + + - name: Upload Nightingale iOS verification artifacts + if: always() + uses: actions/upload-artifact@v4 + with: + name: nightingale-ios-foundation-verification + path: | + ${{ runner.temp }}/NightingaleUnitTests.xcresult + ${{ runner.temp }}/NightingaleUITests.xcresult + ${{ runner.temp }}/nightingale-ios-release/Build/Products/Release-iphonesimulator/Nightingale.app + if-no-files-found: warn + retention-days: 14 + # --------------------------------------------------------------------------- # Hummingbird staff apps — release boundaries, contracts, and native tests # --------------------------------------------------------------------------- @@ -1208,7 +1473,7 @@ jobs: echo "udid=$simulator_udid" >> "$GITHUB_OUTPUT" xcrun simctl boot "$simulator_udid" 2>/dev/null || true - # Nightingale.xcodeproj is COMMITTED, so this job builds it + # HummingbirdPatient.xcodeproj is COMMITTED, so this job builds it # directly — no XcodeGen needed here. The project-drift verify, the # patient source boundary, and the Release transport/boundary chain # run in the parallel REQUIRED sibling 'Hummingbird patient (iOS @@ -1220,8 +1485,8 @@ jobs: - name: Build Debug for testing (iPhone Simulator) run: | xcodebuild \ - -project "$IOS_PATIENT_ROOT/Nightingale.xcodeproj" \ - -scheme Nightingale \ + -project "$IOS_PATIENT_ROOT/HummingbirdPatient.xcodeproj" \ + -scheme HummingbirdPatient \ -configuration Debug \ -sdk iphonesimulator \ -destination "generic/platform=iOS Simulator" \ @@ -1238,13 +1503,13 @@ jobs: - name: Run XCTest unit suite run: | xcodebuild \ - -project "$IOS_PATIENT_ROOT/Nightingale.xcodeproj" \ - -scheme Nightingale \ + -project "$IOS_PATIENT_ROOT/HummingbirdPatient.xcodeproj" \ + -scheme HummingbirdPatient \ -configuration Debug \ -destination "platform=iOS Simulator,id=${{ steps.simulator.outputs.udid }}" \ -derivedDataPath "$RUNNER_TEMP/hummingbird-patient-ios-tests" \ - -resultBundlePath "$RUNNER_TEMP/NightingaleUnitTests.xcresult" \ - -only-testing:NightingaleTests \ + -resultBundlePath "$RUNNER_TEMP/HummingbirdPatientUnitTests.xcresult" \ + -only-testing:HummingbirdPatientTests \ -parallel-testing-enabled NO \ CODE_SIGNING_ALLOWED=NO \ CODE_SIGNING_REQUIRED=NO \ @@ -1257,13 +1522,13 @@ jobs: XCUITEST_RETRY_FLAGS: ${{ github.event_name == 'pull_request' && '-retry-tests-on-failure -test-iterations 3 -test-repetition-relaunch-enabled YES' || '' }} run: | xcodebuild \ - -project "$IOS_PATIENT_ROOT/Nightingale.xcodeproj" \ - -scheme Nightingale \ + -project "$IOS_PATIENT_ROOT/HummingbirdPatient.xcodeproj" \ + -scheme HummingbirdPatient \ -configuration Debug \ -destination "platform=iOS Simulator,id=${{ steps.simulator.outputs.udid }}" \ -derivedDataPath "$RUNNER_TEMP/hummingbird-patient-ios-tests" \ - -resultBundlePath "$RUNNER_TEMP/NightingaleUITests.xcresult" \ - -only-testing:NightingaleUITests \ + -resultBundlePath "$RUNNER_TEMP/HummingbirdPatientUITests.xcresult" \ + -only-testing:HummingbirdPatientUITests \ -parallel-testing-enabled NO \ $XCUITEST_RETRY_FLAGS \ CODE_SIGNING_ALLOWED=NO \ @@ -1280,8 +1545,8 @@ jobs: with: name: hummingbird-patient-ios-verification path: | - ${{ runner.temp }}/NightingaleUnitTests.xcresult - ${{ runner.temp }}/NightingaleUITests.xcresult + ${{ runner.temp }}/HummingbirdPatientUnitTests.xcresult + ${{ runner.temp }}/HummingbirdPatientUITests.xcresult if-no-files-found: warn retention-days: 14 @@ -1328,7 +1593,7 @@ jobs: run: | bash scripts/ci/verify-hummingbird-patient-xcode-project.sh "$IOS_PATIENT_ROOT" bash scripts/ci/verify-hummingbird-patient-boundary.sh source "$IOS_PATIENT_ROOT" - xcodebuild -project "$IOS_PATIENT_ROOT/Nightingale.xcodeproj" -list + xcodebuild -project "$IOS_PATIENT_ROOT/HummingbirdPatient.xcodeproj" -list # ARCHS=arm64: the Release artifact is plist/strings-verified and # uploaded, never executed on x86_64 — the fat x86_64 slice only @@ -1336,8 +1601,8 @@ jobs: - name: Build Release for iPhone Simulator run: | xcodebuild \ - -project "$IOS_PATIENT_ROOT/Nightingale.xcodeproj" \ - -scheme Nightingale \ + -project "$IOS_PATIENT_ROOT/HummingbirdPatient.xcodeproj" \ + -scheme HummingbirdPatient \ -configuration Release \ -sdk iphonesimulator \ -destination "generic/platform=iOS Simulator" \ @@ -1351,7 +1616,7 @@ jobs: - name: Verify patient Release app boundary run: | - release_app="$RUNNER_TEMP/hummingbird-patient-ios-release/Build/Products/Release-iphonesimulator/Nightingale.app" + release_app="$RUNNER_TEMP/hummingbird-patient-ios-release/Build/Products/Release-iphonesimulator/HummingbirdPatient.app" bash scripts/ci/verify-hummingbird-patient-boundary.sh artifact "$release_app" bash scripts/ci/verify-hummingbird-ios-release-transport.sh "$release_app" patient @@ -1360,6 +1625,6 @@ jobs: uses: actions/upload-artifact@v4 with: name: hummingbird-patient-ios-release - path: ${{ runner.temp }}/hummingbird-patient-ios-release/Build/Products/Release-iphonesimulator/Nightingale.app + path: ${{ runner.temp }}/hummingbird-patient-ios-release/Build/Products/Release-iphonesimulator/HummingbirdPatient.app if-no-files-found: warn retention-days: 14 diff --git a/.gitleaks.toml b/.gitleaks.toml index efec4e52..9300af85 100644 --- a/.gitleaks.toml +++ b/.gitleaks.toml @@ -85,22 +85,15 @@ paths = [ regexes = ['''^drgcp-[a-z0-9-]{26,65}$'''] [[allowlists]] -# Nightingale patient-journey devlog: the generic API-key rule captures the -# English phrase "notification/delivery" out of a prose sentence, because the -# word "API" appears inside its keyword window: -# -# "... concrete identifiers or API paths, notification/delivery -# overstatement, offline PHI/queue/stale-current behavior ..." -# -# There is no credential here — the captured value is two ordinary words and a -# slash. Scoped to the exact devlog and that exact phrase; the two SHA-256 -# evidence digests recorded elsewhere in the same file, and every other value -# in it, remain scanned. -# -# The history scan walks all refs, so this fires from any branch for as long as -# codex/nightingale-patient-product exists. The allowlist therefore has to live -# on main rather than only on the branch that introduced the file. -description = "Nightingale devlog prose false positive (notification/delivery)" +# Historical devlog prose used the adjacent words "API paths, notification", +# which the generic-api-key rule parsed as a key/value pair and captured the +# ordinary phrase "notification/delivery". Keep the history exception +# AND-scoped to the two exact documents and exact captured phrase so no +# credential-shaped value is exempted. +description = "Devlog API-path prose false positives" condition = "AND" -paths = ['''^docs/devlog/DEVLOG-nightingale-patient-product-2026-07-26\.md$'''] +paths = [ + '''^docs/devlog/DEVLOG-nightingale-patient-product-2026-07-26\.md$''', + '''^docs/DEVLOG-browser-chain-decouple-2026-07-27\.md$''', +] regexes = ['''^notification/delivery$'''] diff --git a/app/Console/Commands/HummingbirdDraftReferencePatientProjectionsCommand.php b/app/Console/Commands/HummingbirdDraftReferencePatientProjectionsCommand.php index 9e9a526d..a23a1315 100644 --- a/app/Console/Commands/HummingbirdDraftReferencePatientProjectionsCommand.php +++ b/app/Console/Commands/HummingbirdDraftReferencePatientProjectionsCommand.php @@ -2,7 +2,7 @@ namespace App\Console\Commands; -use App\Services\Patient\Demo\NightingaleReferenceProjectionDraftProvisioner; +use App\Services\Patient\Demo\HummingbirdPatientReferenceProjectionDraftProvisioner; use Illuminate\Console\Command; use Throwable; @@ -17,16 +17,16 @@ class HummingbirdDraftReferencePatientProjectionsCommand extends Command protected $description = 'Preview or create non-visible draft projections for the pending Hummingbird reference patient'; - public function handle(NightingaleReferenceProjectionDraftProvisioner $provisioner): int + public function handle(HummingbirdPatientReferenceProjectionDraftProvisioner $provisioner): int { if ($this->option('commit') && ! hash_equals( - NightingaleReferenceProjectionDraftProvisioner::CONFIRMATION, + HummingbirdPatientReferenceProjectionDraftProvisioner::CONFIRMATION, (string) $this->option('confirm-draft-only'), )) { $this->error( '--commit requires --confirm-draft-only=' - .NightingaleReferenceProjectionDraftProvisioner::CONFIRMATION, + .HummingbirdPatientReferenceProjectionDraftProvisioner::CONFIRMATION, ); return self::INVALID; diff --git a/app/Console/Commands/HummingbirdProvisionReferencePatientIdentityCommand.php b/app/Console/Commands/HummingbirdProvisionReferencePatientIdentityCommand.php index 8ef100a7..d7797fe8 100644 --- a/app/Console/Commands/HummingbirdProvisionReferencePatientIdentityCommand.php +++ b/app/Console/Commands/HummingbirdProvisionReferencePatientIdentityCommand.php @@ -2,7 +2,7 @@ namespace App\Console\Commands; -use App\Services\Patient\Demo\NightingaleReferenceIdentityProvisioner; +use App\Services\Patient\Demo\HummingbirdPatientReferenceIdentityProvisioner; use Illuminate\Console\Command; use Throwable; @@ -17,7 +17,7 @@ class HummingbirdProvisionReferencePatientIdentityCommand extends Command protected $description = 'Dry-run or idempotently bind the command-owned synthetic inpatient to Hummingbird Patient enrollment'; - public function handle(NightingaleReferenceIdentityProvisioner $provisioner): int + public function handle(HummingbirdPatientReferenceIdentityProvisioner $provisioner): int { if ($this->option('show-secrets') && ! $this->option('commit')) { $this->error('--show-secrets requires --commit because enrollment material is generated only once.'); diff --git a/app/Http/Controllers/Api/Patient/MessagingController.php b/app/Http/Controllers/Api/Patient/MessagingController.php index 3c629f56..7ff4bb11 100644 --- a/app/Http/Controllers/Api/Patient/MessagingController.php +++ b/app/Http/Controllers/Api/Patient/MessagingController.php @@ -61,10 +61,10 @@ public function store(CreateMessageThreadRequest $request, string $encounterUuid $principal = $request->user(); $input = $request->validated(); $result = match ($input['topic_code'] ?? null) { - 'care_preference' => (bool) config('nightingale.features.care_preferences') + 'care_preference' => (bool) config('hummingbird-patient.features.care_preferences') ? $this->carePreferences->submit($request, $principal, $encounterUuid, $input) : $this->messaging->createThread($request, $principal, $encounterUuid, $input), - 'patient_goal' => (bool) config('nightingale.features.patient_goals') + 'patient_goal' => (bool) config('hummingbird-patient.features.patient_goals') ? $this->patientGoals->submit($request, $principal, $encounterUuid, $input) : $this->messaging->createThread($request, $principal, $encounterUuid, $input), default => $this->messaging->createThread($request, $principal, $encounterUuid, $input), diff --git a/app/Http/Middleware/EnsureNightingaleEnabled.php b/app/Http/Middleware/EnsureHummingbirdPatientEnabled.php similarity index 88% rename from app/Http/Middleware/EnsureNightingaleEnabled.php rename to app/Http/Middleware/EnsureHummingbirdPatientEnabled.php index d6b051ff..6cb47a5f 100644 --- a/app/Http/Middleware/EnsureNightingaleEnabled.php +++ b/app/Http/Middleware/EnsureHummingbirdPatientEnabled.php @@ -8,13 +8,13 @@ use Illuminate\Http\Request; use Symfony\Component\HttpFoundation\Response; -class EnsureNightingaleEnabled +class EnsureHummingbirdPatientEnabled { public function __construct(private readonly PatientHmac $hmac) {} public function handle(Request $request, Closure $next): Response { - if (! config('nightingale.enabled', false)) { + if (! config('hummingbird-patient.enabled', false)) { return $this->notFound(); } diff --git a/app/Http/Middleware/EnsureNightingaleFeatureEnabled.php b/app/Http/Middleware/EnsureHummingbirdPatientFeatureEnabled.php similarity index 83% rename from app/Http/Middleware/EnsureNightingaleFeatureEnabled.php rename to app/Http/Middleware/EnsureHummingbirdPatientFeatureEnabled.php index 36930fdc..0d2fec0a 100644 --- a/app/Http/Middleware/EnsureNightingaleFeatureEnabled.php +++ b/app/Http/Middleware/EnsureHummingbirdPatientFeatureEnabled.php @@ -7,11 +7,11 @@ use Illuminate\Http\Request; use Symfony\Component\HttpFoundation\Response; -class EnsureNightingaleFeatureEnabled +class EnsureHummingbirdPatientFeatureEnabled { public function handle(Request $request, Closure $next, string $feature): Response { - if (! config("nightingale.features.{$feature}", false)) { + if (! config("hummingbird-patient.features.{$feature}", false)) { return $this->notFound(); } diff --git a/app/Http/Middleware/EnsurePatientStaffMessagingEnabled.php b/app/Http/Middleware/EnsurePatientStaffMessagingEnabled.php index 2d95fbb6..3576cc72 100644 --- a/app/Http/Middleware/EnsurePatientStaffMessagingEnabled.php +++ b/app/Http/Middleware/EnsurePatientStaffMessagingEnabled.php @@ -11,10 +11,10 @@ class EnsurePatientStaffMessagingEnabled { public function handle(Request $request, Closure $next): Response { - if (! (bool) config('nightingale.enabled', false) - || ! (bool) config('nightingale.features.messaging', false) - || ! (bool) config('nightingale.staff_messaging.enabled', false) - || config('nightingale.staff_messaging.governance_status') !== 'approved' + if (! (bool) config('hummingbird-patient.enabled', false) + || ! (bool) config('hummingbird-patient.features.messaging', false) + || ! (bool) config('hummingbird-patient.staff_messaging.enabled', false) + || config('hummingbird-patient.staff_messaging.governance_status') !== 'approved' ) { return $this->notFound(); } diff --git a/app/Http/Middleware/HandleInertiaRequests.php b/app/Http/Middleware/HandleInertiaRequests.php index 308adaa6..7955e07c 100644 --- a/app/Http/Middleware/HandleInertiaRequests.php +++ b/app/Http/Middleware/HandleInertiaRequests.php @@ -117,10 +117,10 @@ public function share(Request $request): array 'home_hospital' => (bool) config('home_hospital.enabled'), // The staff workspace is visible only when the whole patient // messaging chain and its independent governance gate are live. - 'patient_communications' => (bool) config('nightingale.enabled', false) - && (bool) config('nightingale.features.messaging', false) - && (bool) config('nightingale.staff_messaging.enabled', false) - && config('nightingale.staff_messaging.governance_status') === 'approved', + 'patient_communications' => (bool) config('hummingbird-patient.enabled', false) + && (bool) config('hummingbird-patient.features.messaging', false) + && (bool) config('hummingbird-patient.staff_messaging.enabled', false) + && config('hummingbird-patient.staff_messaging.governance_status') === 'approved', ], 'workflow' => $request->session()->get('workflow'), 'flash' => [ diff --git a/app/Nightingale/Activation/NightingaleActivationDisposition.php b/app/Nightingale/Activation/NightingaleActivationDisposition.php new file mode 100644 index 00000000..bca0699b --- /dev/null +++ b/app/Nightingale/Activation/NightingaleActivationDisposition.php @@ -0,0 +1,15 @@ +permitsFurtherEvaluation() + || ! $contentRelease->permitsFurtherEvaluation() + || ! $featureActivation->permitsFurtherEvaluation() + || ! $pilotEnrollment->permitsFurtherEvaluation() + || ! $sourceConnector->permitsFurtherEvaluation() + ) { + return NightingaleActivationDisposition::Hold; + } + + return NightingaleActivationDisposition::ContinueToOperationSpecificReleaseEvaluation; + } +} diff --git a/app/Nightingale/Activation/NightingaleClinicalApprovalState.php b/app/Nightingale/Activation/NightingaleClinicalApprovalState.php new file mode 100644 index 00000000..e6fc1628 --- /dev/null +++ b/app/Nightingale/Activation/NightingaleClinicalApprovalState.php @@ -0,0 +1,21 @@ + [ + 'status' => 404, + 'code' => 'not_found', + 'cache_control' => 'private, no-store, max-age=0', + ], + self::ContinueToGovernedProjectionEvaluation => null, + }; + } +} diff --git a/app/Nightingale/Disclosure/NightingaleEncounterBindingState.php b/app/Nightingale/Disclosure/NightingaleEncounterBindingState.php new file mode 100644 index 00000000..9519f559 --- /dev/null +++ b/app/Nightingale/Disclosure/NightingaleEncounterBindingState.php @@ -0,0 +1,20 @@ +permitsGovernedEvaluation() + || ! $encounterBinding->permitsGovernedEvaluation() + || ! $resource->permitsGovernedEvaluation() + ) { + return NightingaleDisclosureDisposition::WithholdNotFound; + } + + return NightingaleDisclosureDisposition::ContinueToGovernedProjectionEvaluation; + } +} diff --git a/app/Nightingale/Disclosure/NightingaleRelationshipState.php b/app/Nightingale/Disclosure/NightingaleRelationshipState.php new file mode 100644 index 00000000..a9fb600b --- /dev/null +++ b/app/Nightingale/Disclosure/NightingaleRelationshipState.php @@ -0,0 +1,25 @@ +state()->permitsGovernedEvaluation() + || ! $inpatientSource->state()->permitsGovernedEvaluation() + ) { + return NightingalePreconditionDisposition::Withhold; + } + + return NightingalePreconditionDisposition::ContinueToGovernedEvaluation; + } +} diff --git a/app/Nightingale/EncounterAccess/NightingalePreconditionDisposition.php b/app/Nightingale/EncounterAccess/NightingalePreconditionDisposition.php new file mode 100644 index 00000000..b7741068 --- /dev/null +++ b/app/Nightingale/EncounterAccess/NightingalePreconditionDisposition.php @@ -0,0 +1,14 @@ +released_at !== null && $projection->released_at->isPast() && $policy !== null - && $policy->version === (string) config('nightingale.policy_version') + && $policy->version === (string) config('hummingbird-patient.policy_version') && $policy->status === 'active' && $policy->approved_at !== null && $policy->effective_from !== null diff --git a/app/Services/Mobile/MobileForYouService.php b/app/Services/Mobile/MobileForYouService.php index ce3c0173..71e39589 100644 --- a/app/Services/Mobile/MobileForYouService.php +++ b/app/Services/Mobile/MobileForYouService.php @@ -318,10 +318,10 @@ private function patientCommunicationItems(User $user): Collection private function patientCommunicationsEnabled(): bool { - return (bool) config('nightingale.enabled', false) - && (bool) config('nightingale.features.messaging', false) - && (bool) config('nightingale.staff_messaging.enabled', false) - && config('nightingale.staff_messaging.governance_status') === 'approved'; + return (bool) config('hummingbird-patient.enabled', false) + && (bool) config('hummingbird-patient.features.messaging', false) + && (bool) config('hummingbird-patient.staff_messaging.enabled', false) + && config('hummingbird-patient.staff_messaging.governance_status') === 'approved'; } /** diff --git a/app/Services/Patient/Demo/NightingaleReferenceIdentityProvisioner.php b/app/Services/Patient/Demo/HummingbirdPatientReferenceIdentityProvisioner.php similarity index 98% rename from app/Services/Patient/Demo/NightingaleReferenceIdentityProvisioner.php rename to app/Services/Patient/Demo/HummingbirdPatientReferenceIdentityProvisioner.php index f19ed27e..767e80a5 100644 --- a/app/Services/Patient/Demo/NightingaleReferenceIdentityProvisioner.php +++ b/app/Services/Patient/Demo/HummingbirdPatientReferenceIdentityProvisioner.php @@ -20,7 +20,7 @@ use RuntimeException; use Throwable; -final class NightingaleReferenceIdentityProvisioner +final class HummingbirdPatientReferenceIdentityProvisioner { public const OWNER = 'hummingbird-patient-reference-identity-provisioner-v1'; @@ -145,7 +145,7 @@ public function provision( private function assertExecutionSafe(): void { - if (! (bool) config('nightingale.reference_provisioning.enabled', false)) { + if (! (bool) config('hummingbird-patient.reference_provisioning.enabled', false)) { throw new RuntimeException('reference_patient_provisioning_disabled'); } @@ -173,7 +173,7 @@ private function assertExecutionSafe(): void private function assertEncryptionConfiguration(): void { - $keyVersion = trim((string) config('nightingale.reference_provisioning.encryption_key_version')); + $keyVersion = trim((string) config('hummingbird-patient.reference_provisioning.encryption_key_version')); if (preg_match('/^[a-zA-Z0-9][a-zA-Z0-9._-]{2,63}$/', $keyVersion) !== 1) { throw new RuntimeException('reference_patient_encryption_key_version_unavailable'); } @@ -493,7 +493,7 @@ private function issuePlatformChallenges( 'delivery_method' => 'in_person', 'status' => 'issued', 'failed_attempts' => 0, - 'max_attempts' => (int) config('nightingale.enrollment.max_attempts', 5), + 'max_attempts' => (int) config('hummingbird-patient.enrollment.max_attempts', 5), 'expires_at' => $expiresAt, 'metadata' => [ 'owner' => self::OWNER, @@ -640,13 +640,13 @@ private function assertGrantFoundation( private function encryptionKeyVersion(): string { - return trim((string) config('nightingale.reference_provisioning.encryption_key_version')); + return trim((string) config('hummingbird-patient.reference_provisioning.encryption_key_version')); } private function challengeTtlMinutes(): int { return max(5, min(30, (int) config( - 'nightingale.reference_provisioning.challenge_ttl_minutes', + 'hummingbird-patient.reference_provisioning.challenge_ttl_minutes', 10, ))); } diff --git a/app/Services/Patient/Demo/NightingaleReferenceProjectionDraftProvisioner.php b/app/Services/Patient/Demo/HummingbirdPatientReferenceProjectionDraftProvisioner.php similarity index 99% rename from app/Services/Patient/Demo/NightingaleReferenceProjectionDraftProvisioner.php rename to app/Services/Patient/Demo/HummingbirdPatientReferenceProjectionDraftProvisioner.php index 989e6667..4d7b5075 100644 --- a/app/Services/Patient/Demo/NightingaleReferenceProjectionDraftProvisioner.php +++ b/app/Services/Patient/Demo/HummingbirdPatientReferenceProjectionDraftProvisioner.php @@ -38,7 +38,7 @@ * projections under an effective grant and active release policy, so these * rows remain unavailable to every patient API request. */ -final class NightingaleReferenceProjectionDraftProvisioner +final class HummingbirdPatientReferenceProjectionDraftProvisioner { public const OWNER = 'hummingbird-patient-reference-projection-draft-provisioner-v1'; @@ -248,7 +248,7 @@ private function foundation(string $patientRef, ?int $encounterId, bool $forUpda $principalQuery = PatientPrincipal::query() ->whereRaw("preferences #>> '{provisioning,owner}' = ?", [ - NightingaleReferenceIdentityProvisioner::OWNER, + HummingbirdPatientReferenceIdentityProvisioner::OWNER, ]); if ($forUpdate) { $principalQuery->lockForUpdate(); @@ -270,7 +270,7 @@ private function foundation(string $patientRef, ?int $encounterId, bool $forUpda $grantQuery = PatientEncounterAccessGrant::query() ->where('source_encounter_id', $encounter->getKey()) ->whereRaw("metadata->>'owner' = ?", [ - NightingaleReferenceIdentityProvisioner::OWNER, + HummingbirdPatientReferenceIdentityProvisioner::OWNER, ]); if ($forUpdate) { $grantQuery->lockForUpdate(); @@ -301,7 +301,7 @@ private function foundation(string $patientRef, ?int $encounterId, bool $forUpda || $identity->status !== 'verified' || $identity->revoked_at !== null || ($identity->provenance['owner'] ?? null) - !== NightingaleReferenceIdentityProvisioner::OWNER + !== HummingbirdPatientReferenceIdentityProvisioner::OWNER || ($identity->provenance['synthetic'] ?? null) !== true) { throw new RuntimeException('reference_patient_projection_identity_not_safe'); } diff --git a/app/Services/Patient/Education/PatientEducationClarificationService.php b/app/Services/Patient/Education/PatientEducationClarificationService.php index f44d9cc8..02a64c95 100644 --- a/app/Services/Patient/Education/PatientEducationClarificationService.php +++ b/app/Services/Patient/Education/PatientEducationClarificationService.php @@ -43,7 +43,7 @@ public function requestClarification( string $educationItemUuid, array $input, ): array { - if (! (bool) config('nightingale.features.messaging')) { + if (! (bool) config('hummingbird-patient.features.messaging')) { throw PatientMessagingFailure::notFound(); } @@ -204,7 +204,7 @@ private function releasedEducationContext( $policy = PatientReleasePolicyVersion::query() ->effective() - ->where('version', (string) config('nightingale.policy_version')) + ->where('version', (string) config('hummingbird-patient.policy_version')) ->first(); if (! $policy instanceof PatientReleasePolicyVersion) { diff --git a/app/Services/Patient/Goals/PatientAuthoredGoalService.php b/app/Services/Patient/Goals/PatientAuthoredGoalService.php index db2f2271..4a0f85f2 100644 --- a/app/Services/Patient/Goals/PatientAuthoredGoalService.php +++ b/app/Services/Patient/Goals/PatientAuthoredGoalService.php @@ -40,7 +40,7 @@ public function submit( string $encounterUuid, array $input, ): array { - if (! (bool) config('nightingale.features.patient_goals') + if (! (bool) config('hummingbird-patient.features.patient_goals') || ($input['topic_code'] ?? null) !== self::TOPIC_CODE) { throw PatientMessagingFailure::notFound(); } diff --git a/app/Services/Patient/Messaging/DatabasePatientMessageHandoffConsumer.php b/app/Services/Patient/Messaging/DatabasePatientMessageHandoffConsumer.php index e9cdd351..38eadc74 100644 --- a/app/Services/Patient/Messaging/DatabasePatientMessageHandoffConsumer.php +++ b/app/Services/Patient/Messaging/DatabasePatientMessageHandoffConsumer.php @@ -92,7 +92,7 @@ public function routableForGrant( */ public function consumeBatch(string $workerRef, ?int $limit = null): array { - $policyVersion = trim((string) config('nightingale.messaging.policy_version')); + $policyVersion = trim((string) config('hummingbird-patient.messaging.policy_version')); if (! $this->infrastructureReady()) { throw new RuntimeException('patient_message_handoff_not_ready'); } @@ -107,7 +107,7 @@ public function consumeBatch(string $workerRef, ?int $limit = null): array 0, ); - $limit = max(1, min(500, $limit ?? (int) config('nightingale.staff_messaging.batch_size', 100))); + $limit = max(1, min(500, $limit ?? (int) config('hummingbird-patient.staff_messaging.batch_size', 100))); $outboxIds = PatientNotificationOutbox::query() ->where('destination', 'staff_inbox') ->where('available_at', '<=', now()) @@ -403,13 +403,13 @@ private function configurationReady(string $policyVersion): bool { if (! $this->infrastructureReady() || $policyVersion === '' - || config('nightingale.messaging.governance_status') !== 'approved' + || config('hummingbird-patient.messaging.governance_status') !== 'approved' || $this->pilotUnitIds() === [] ) { return false; } - $topics = config('nightingale.messaging.topics'); + $topics = config('hummingbird-patient.messaging.topics'); if (! is_array($topics) || $topics === []) { return false; } @@ -436,8 +436,8 @@ private function configurationReady(string $policyVersion): bool private function infrastructureReady(): bool { - return (bool) config('nightingale.staff_messaging.enabled', false) - && config('nightingale.staff_messaging.governance_status') === 'approved' + return (bool) config('hummingbird-patient.staff_messaging.enabled', false) + && config('hummingbird-patient.staff_messaging.governance_status') === 'approved' && ! collect(self::REQUIRED_TABLES)->contains( fn (string $table): bool => ! Schema::hasTable($table), ); @@ -545,7 +545,7 @@ private function writeHeartbeat( /** @return list */ private function pilotUnitIds(): array { - return collect(config('nightingale.staff_messaging.pilot_unit_ids', [])) + return collect(config('hummingbird-patient.staff_messaging.pilot_unit_ids', [])) ->map(static fn (mixed $id): int => (int) $id) ->filter(static fn (int $id): bool => $id > 0) ->unique() @@ -561,12 +561,12 @@ private function poolDigest(string $policyVersion, string $poolKey): string private function consumerKey(): string { - return (string) config('nightingale.staff_messaging.consumer_key', 'patient-message-staff-inbox-v1'); + return (string) config('hummingbird-patient.staff_messaging.consumer_key', 'patient-message-staff-inbox-v1'); } private function heartbeatTtlSeconds(): int { - return max(30, min(600, (int) config('nightingale.staff_messaging.heartbeat_ttl_seconds', 120))); + return max(30, min(600, (int) config('hummingbird-patient.staff_messaging.heartbeat_ttl_seconds', 120))); } private function safeErrorCode(Throwable $exception): string diff --git a/app/Services/Patient/Messaging/PatientCommunicationEscalationService.php b/app/Services/Patient/Messaging/PatientCommunicationEscalationService.php index 7e9eea80..4a02b2a5 100644 --- a/app/Services/Patient/Messaging/PatientCommunicationEscalationService.php +++ b/app/Services/Patient/Messaging/PatientCommunicationEscalationService.php @@ -25,8 +25,8 @@ public function __construct(private readonly PatientHmac $hmac) {} /** @return array{selected: int, escalated: int, skipped: int, failed: int} */ public function escalateDue(?int $limit = null): array { - if (! (bool) config('nightingale.staff_messaging.enabled', false) - || config('nightingale.staff_messaging.governance_status') !== 'approved' + if (! (bool) config('hummingbird-patient.staff_messaging.enabled', false) + || config('hummingbird-patient.staff_messaging.governance_status') !== 'approved' ) { throw StaffPatientCommunicationFailure::unavailable(); } diff --git a/app/Services/Patient/Messaging/PatientCommunicationLifecycleReconciliationService.php b/app/Services/Patient/Messaging/PatientCommunicationLifecycleReconciliationService.php index 79f1a816..7f32e9ad 100644 --- a/app/Services/Patient/Messaging/PatientCommunicationLifecycleReconciliationService.php +++ b/app/Services/Patient/Messaging/PatientCommunicationLifecycleReconciliationService.php @@ -570,10 +570,10 @@ private function systemActorDigest(): string private function assertAvailable(): void { - if (! (bool) config('nightingale.enabled', false) - || ! (bool) config('nightingale.features.messaging', false) - || ! (bool) config('nightingale.staff_messaging.enabled', false) - || config('nightingale.staff_messaging.governance_status') !== 'approved' + if (! (bool) config('hummingbird-patient.enabled', false) + || ! (bool) config('hummingbird-patient.features.messaging', false) + || ! (bool) config('hummingbird-patient.staff_messaging.enabled', false) + || config('hummingbird-patient.staff_messaging.governance_status') !== 'approved' || collect(self::REQUIRED_TABLES)->contains( fn (string $table): bool => ! Schema::hasTable($table), ) diff --git a/app/Services/Patient/Messaging/PatientCommunicationPoolResolver.php b/app/Services/Patient/Messaging/PatientCommunicationPoolResolver.php index 773921e6..6015ae8f 100644 --- a/app/Services/Patient/Messaging/PatientCommunicationPoolResolver.php +++ b/app/Services/Patient/Messaging/PatientCommunicationPoolResolver.php @@ -388,7 +388,7 @@ private function validFacilityKey(string $facilityKey): bool /** @return list */ private function pilotUnitIds(): array { - return collect(config('nightingale.staff_messaging.pilot_unit_ids', [])) + return collect(config('hummingbird-patient.staff_messaging.pilot_unit_ids', [])) ->map(static fn (mixed $id): int => (int) $id) ->filter(static fn (int $id): bool => $id > 0) ->unique() diff --git a/app/Services/Patient/Messaging/PatientMessageCipher.php b/app/Services/Patient/Messaging/PatientMessageCipher.php index 7ecf25d2..2b1f194e 100644 --- a/app/Services/Patient/Messaging/PatientMessageCipher.php +++ b/app/Services/Patient/Messaging/PatientMessageCipher.php @@ -30,7 +30,7 @@ public function assertAvailable(string $keyVersion): void public function encrypt(string $plaintext, string $keyVersion, string $context): string { $configuredVersion = trim((string) config( - 'nightingale.messaging.encryption_key_version', + 'hummingbird-patient.messaging.encryption_key_version', )); if ($configuredVersion === '' || ! hash_equals($configuredVersion, $keyVersion)) { @@ -97,10 +97,10 @@ private function encrypter(string $keyVersion): Encrypter private function keyForVersion(string $keyVersion): string { $currentVersion = trim((string) config( - 'nightingale.messaging.encryption_key_version', + 'hummingbird-patient.messaging.encryption_key_version', )); $configured = $currentVersion !== '' && hash_equals($currentVersion, $keyVersion) - ? config('nightingale.messaging.encryption_key') + ? config('hummingbird-patient.messaging.encryption_key') : $this->previousKey($keyVersion); if (is_string($configured) && trim($configured) !== '') { @@ -116,7 +116,7 @@ private function keyForVersion(string $keyVersion): string private function previousKey(string $keyVersion): mixed { - $encoded = config('nightingale.messaging.previous_encryption_keys_json'); + $encoded = config('hummingbird-patient.messaging.previous_encryption_keys_json'); if (! is_string($encoded) || trim($encoded) === '') { return null; } diff --git a/app/Services/Patient/Messaging/PatientMessagingPolicyRegistry.php b/app/Services/Patient/Messaging/PatientMessagingPolicyRegistry.php index 2dfb787c..11b230f8 100644 --- a/app/Services/Patient/Messaging/PatientMessagingPolicyRegistry.php +++ b/app/Services/Patient/Messaging/PatientMessagingPolicyRegistry.php @@ -29,7 +29,7 @@ public function __construct( */ public function disclosurePolicy(): array { - $config = (array) config('nightingale.messaging', []); + $config = (array) config('hummingbird-patient.messaging', []); if (($config['governance_status'] ?? null) !== 'approved') { throw PatientMessagingFailure::unavailable(); @@ -60,7 +60,7 @@ public function disclosurePolicy(): array // patient-composition gate. Filtering it here means neither the // topic list nor create-thread accepts it while disabled. if ($code === 'rounds_question' - && ! (bool) config('nightingale.features.rounds_questions') + && ! (bool) config('hummingbird-patient.features.rounds_questions') ) { continue; } @@ -97,7 +97,7 @@ public function disclosurePolicy(): array */ public function mutationPolicy(): array { - $config = (array) config('nightingale.messaging', []); + $config = (array) config('hummingbird-patient.messaging', []); $policy = $this->contentWritePolicy(); if (! $this->consumer($config)->readyForPolicy($policy['policy_version'])) { @@ -116,7 +116,7 @@ public function mutationPolicy(): array */ public function contentWritePolicy(): array { - $config = (array) config('nightingale.messaging', []); + $config = (array) config('hummingbird-patient.messaging', []); $policy = $this->disclosurePolicy(); $policy['encryption_key_version'] = $this->requiredString( $config, @@ -175,7 +175,7 @@ public function assertMutationRoutable( if (! is_string($policyVersion) || ! is_string($poolKey) - || ! $this->consumer((array) config('nightingale.messaging', []))->routableForGrant( + || ! $this->consumer((array) config('hummingbird-patient.messaging', []))->routableForGrant( $policyVersion, $topicCode, $poolKey, diff --git a/app/Services/Patient/Messaging/StaffPatientCommunicationService.php b/app/Services/Patient/Messaging/StaffPatientCommunicationService.php index 5e34e7ed..a8bc6360 100644 --- a/app/Services/Patient/Messaging/StaffPatientCommunicationService.php +++ b/app/Services/Patient/Messaging/StaffPatientCommunicationService.php @@ -993,10 +993,10 @@ public function close(Request $request, User $user, string $workItemUuid, array private function assertAvailable(): void { - if (! (bool) config('nightingale.enabled', false) - || ! (bool) config('nightingale.features.messaging', false) - || ! (bool) config('nightingale.staff_messaging.enabled', false) - || config('nightingale.staff_messaging.governance_status') !== 'approved' + if (! (bool) config('hummingbird-patient.enabled', false) + || ! (bool) config('hummingbird-patient.features.messaging', false) + || ! (bool) config('hummingbird-patient.staff_messaging.enabled', false) + || config('hummingbird-patient.staff_messaging.governance_status') !== 'approved' || ! $this->schemaReady() ) { throw StaffPatientCommunicationFailure::unavailable(); diff --git a/app/Services/Patient/Pathway/PatientPathwaySourceReconciliationService.php b/app/Services/Patient/Pathway/PatientPathwaySourceReconciliationService.php index 59f3f38e..76a79ba9 100644 --- a/app/Services/Patient/Pathway/PatientPathwaySourceReconciliationService.php +++ b/app/Services/Patient/Pathway/PatientPathwaySourceReconciliationService.php @@ -154,9 +154,9 @@ public function reconcile( private function assertAvailable(): void { if (DB::getDriverName() !== 'pgsql' - || ! (bool) config('nightingale.enabled') - || ! (bool) config('nightingale.features.pathway') - || ! (bool) config('nightingale.features.pathway_source_reconciliation') + || ! (bool) config('hummingbird-patient.enabled') + || ! (bool) config('hummingbird-patient.features.pathway') + || ! (bool) config('hummingbird-patient.features.pathway_source_reconciliation') || ! (bool) config('care-pathways.patient_enabled') || ! (bool) config('care-pathways.assignment_enabled')) { throw new RuntimeException('patient_pathway_source_reconciliation_unavailable'); @@ -169,7 +169,7 @@ private function assertApprovedSource(string $sourceSystemKey): void { if (! in_array( $sourceSystemKey, - (array) config('nightingale.pathway_source_reconciliation.approved_sources', []), + (array) config('hummingbird-patient.pathway_source_reconciliation.approved_sources', []), true, )) { throw new InvalidArgumentException('patient_pathway_source_not_approved'); diff --git a/app/Services/Patient/PatientAuthService.php b/app/Services/Patient/PatientAuthService.php index 52a032c6..bb6fd195 100644 --- a/app/Services/Patient/PatientAuthService.php +++ b/app/Services/Patient/PatientAuthService.php @@ -499,7 +499,7 @@ private function issueNewSession( ): array { $sessionUuid = (string) Str::uuid7(); $refreshExpiresAt = CarbonImmutable::now()->addDays( - (int) config('nightingale.token.refresh_ttl_days', 14), + (int) config('hummingbird-patient.token.refresh_ttl_days', 14), ); $session = PatientSession::query()->create([ @@ -546,9 +546,9 @@ private function issueForSession( PatientSession $session, Request $request, ): array { - $accessTtlMinutes = (int) config('nightingale.token.access_ttl_minutes', 15); + $accessTtlMinutes = (int) config('hummingbird-patient.token.access_ttl_minutes', 15); $configuredRefreshExpiry = CarbonImmutable::now()->addDays( - (int) config('nightingale.token.refresh_ttl_days', 14), + (int) config('hummingbird-patient.token.refresh_ttl_days', 14), ); $sessionExpiry = CarbonImmutable::instance($session->expires_at); $refreshExpiresAt = $configuredRefreshExpiry->lessThan($sessionExpiry) diff --git a/app/Services/Patient/PatientHmac.php b/app/Services/Patient/PatientHmac.php index 9a3707ef..cc93516e 100644 --- a/app/Services/Patient/PatientHmac.php +++ b/app/Services/Patient/PatientHmac.php @@ -27,7 +27,7 @@ public function assertAvailable(): void private function secret(): string { - $configured = trim((string) config('nightingale.hmac_secret')); + $configured = trim((string) config('hummingbird-patient.hmac_secret')); if (strlen($configured) >= 32) { return $configured; diff --git a/app/Services/Patient/PatientNotificationDeviceCipher.php b/app/Services/Patient/PatientNotificationDeviceCipher.php index e98773aa..63c8f556 100644 --- a/app/Services/Patient/PatientNotificationDeviceCipher.php +++ b/app/Services/Patient/PatientNotificationDeviceCipher.php @@ -24,7 +24,7 @@ public function assertAvailable(string $keyVersion): void public function encrypt(string $token, string $keyVersion, string $context): string { $configuredVersion = trim((string) config( - 'nightingale.notification_devices.encryption_key_version', + 'hummingbird-patient.notification_devices.encryption_key_version', )); if ($configuredVersion === '' || ! hash_equals($configuredVersion, $keyVersion)) { throw new RuntimeException('patient_notification_device_encryption_key_not_current'); @@ -89,10 +89,10 @@ private function encrypter(string $keyVersion): Encrypter private function keyForVersion(string $keyVersion): string { $currentVersion = trim((string) config( - 'nightingale.notification_devices.encryption_key_version', + 'hummingbird-patient.notification_devices.encryption_key_version', )); $configured = $currentVersion !== '' && hash_equals($currentVersion, $keyVersion) - ? config('nightingale.notification_devices.encryption_key') + ? config('hummingbird-patient.notification_devices.encryption_key') : $this->previousKey($keyVersion); if (is_string($configured) && trim($configured) !== '') { @@ -108,7 +108,7 @@ private function keyForVersion(string $keyVersion): string private function previousKey(string $keyVersion): mixed { - $encoded = config('nightingale.notification_devices.previous_encryption_keys_json'); + $encoded = config('hummingbird-patient.notification_devices.previous_encryption_keys_json'); if (! is_string($encoded) || trim($encoded) === '') { return null; } diff --git a/app/Services/Patient/PatientNotificationDeviceRegistry.php b/app/Services/Patient/PatientNotificationDeviceRegistry.php index ef2fc747..af7dfd01 100644 --- a/app/Services/Patient/PatientNotificationDeviceRegistry.php +++ b/app/Services/Patient/PatientNotificationDeviceRegistry.php @@ -35,7 +35,7 @@ public function register( ): PatientNotificationDevice { $deviceUuid = $this->canonicalUuid($deviceUuid); $keyVersion = trim((string) config( - 'nightingale.notification_devices.encryption_key_version', + 'hummingbird-patient.notification_devices.encryption_key_version', )); try { diff --git a/app/Services/Patient/PatientResponseMetadata.php b/app/Services/Patient/PatientResponseMetadata.php index 5e774b5f..4a613fba 100644 --- a/app/Services/Patient/PatientResponseMetadata.php +++ b/app/Services/Patient/PatientResponseMetadata.php @@ -20,7 +20,7 @@ public function forRequest(Request $request, array $overrides = []): array 'observed_at' => null, ], 'policy_version' => (string) config( - 'nightingale.policy_version', + 'hummingbird-patient.policy_version', 'patient-disclosure-v1-draft', ), // Stable state codes are rendered with native bundled patient diff --git a/app/Services/Patient/Preferences/PatientCarePreferenceService.php b/app/Services/Patient/Preferences/PatientCarePreferenceService.php index 654e3454..356f2b9c 100644 --- a/app/Services/Patient/Preferences/PatientCarePreferenceService.php +++ b/app/Services/Patient/Preferences/PatientCarePreferenceService.php @@ -41,7 +41,7 @@ public function submit( string $encounterUuid, array $input, ): array { - if (! (bool) config('nightingale.features.care_preferences')) { + if (! (bool) config('hummingbird-patient.features.care_preferences')) { throw PatientMessagingFailure::notFound(); } diff --git a/app/Services/Patient/Projection/PatientPathwayHistoryDraftService.php b/app/Services/Patient/Projection/PatientPathwayHistoryDraftService.php index b7b33c52..0bce7239 100644 --- a/app/Services/Patient/Projection/PatientPathwayHistoryDraftService.php +++ b/app/Services/Patient/Projection/PatientPathwayHistoryDraftService.php @@ -67,7 +67,7 @@ public function draft(PatientPathwayInstance $instance): array $policy = PatientReleasePolicyVersion::query() ->effective() - ->where('version', (string) config('nightingale.policy_version')) + ->where('version', (string) config('hummingbird-patient.policy_version')) ->lockForUpdate() ->first(); if (! $policy instanceof PatientReleasePolicyVersion) { @@ -83,7 +83,7 @@ public function draft(PatientPathwayInstance $instance): array $sourceObservedAt = $this->latestObservation($lockedInstance, $stages, $milestones); $content = $this->content($stages, $milestones); $sourceVersion = (string) config( - 'nightingale.pathway_history_drafts.producer_version', + 'hummingbird-patient.pathway_history_drafts.producer_version', 'patient-pathway-history-draft-v1', ); $guard = app(PatientProjectionContentGuard::class); @@ -113,7 +113,7 @@ public function draft(PatientPathwayInstance $instance): array $cursor = PatientProjectionCursor::query()->create([ 'source_system_key' => (string) config( - 'nightingale.pathway_history_drafts.source_system_key', + 'hummingbird-patient.pathway_history_drafts.source_system_key', 'care-pathways.pathway-history-v1', ), 'projection_kind' => self::PROJECTION_KIND, @@ -211,9 +211,9 @@ public function draftPending(int $limit): array private function assertAvailable(): void { if (DB::getDriverName() !== 'pgsql' - || ! (bool) config('nightingale.enabled') - || ! (bool) config('nightingale.features.pathway') - || ! (bool) config('nightingale.features.pathway_history_drafts') + || ! (bool) config('hummingbird-patient.enabled') + || ! (bool) config('hummingbird-patient.features.pathway') + || ! (bool) config('hummingbird-patient.features.pathway_history_drafts') || ! (bool) config('care-pathways.patient_enabled')) { throw new RuntimeException('patient_pathway_history_drafts_unavailable'); } @@ -240,7 +240,7 @@ private function recordFailure(PatientPathwayInstance $instance, Throwable $exce ); if (PatientProjectionFailure::query() ->where('source_system_key', (string) config( - 'nightingale.pathway_history_drafts.source_system_key', + 'hummingbird-patient.pathway_history_drafts.source_system_key', 'care-pathways.pathway-history-v1', )) ->where('projection_kind', self::PROJECTION_KIND) @@ -252,7 +252,7 @@ private function recordFailure(PatientPathwayInstance $instance, Throwable $exce PatientProjectionFailure::query()->create([ 'source_system_key' => (string) config( - 'nightingale.pathway_history_drafts.source_system_key', + 'hummingbird-patient.pathway_history_drafts.source_system_key', 'care-pathways.pathway-history-v1', ), 'projection_kind' => self::PROJECTION_KIND, @@ -437,11 +437,11 @@ private function cursorDigest( private function freshness(Carbon $sourceObservedAt): string { $minutes = max(0, $sourceObservedAt->diffInMinutes(now())); - if ($minutes <= (int) config('nightingale.pathway_history_drafts.current_after_minutes', 30)) { + if ($minutes <= (int) config('hummingbird-patient.pathway_history_drafts.current_after_minutes', 30)) { return 'current'; } - return $minutes <= (int) config('nightingale.pathway_history_drafts.stale_after_minutes', 240) + return $minutes <= (int) config('hummingbird-patient.pathway_history_drafts.stale_after_minutes', 240) ? 'aging' : 'stale'; } diff --git a/app/Services/Patient/Projection/PatientPathwayProjectionReviewReleaseService.php b/app/Services/Patient/Projection/PatientPathwayProjectionReviewReleaseService.php index e0314415..90919bc8 100644 --- a/app/Services/Patient/Projection/PatientPathwayProjectionReviewReleaseService.php +++ b/app/Services/Patient/Projection/PatientPathwayProjectionReviewReleaseService.php @@ -107,7 +107,7 @@ public function release(User $releaseManager, PatientEncounterProjection $draft) ->first(); $releasedAt = now(); $producerVersion = (string) config( - 'nightingale.pathway_history_releases.producer_version', + 'hummingbird-patient.pathway_history_releases.producer_version', 'patient-pathway-history-clinical-release-v1', ); $release = PatientEncounterProjection::query()->create([ @@ -220,7 +220,7 @@ private function lockedDraftContext(PatientEncounterProjection $draft): array || $lockedDraft->release_state !== 'draft' || $lockedDraft->released_at !== null || $lockedDraft->source_version !== (string) config( - 'nightingale.pathway_history_drafts.producer_version', + 'hummingbird-patient.pathway_history_drafts.producer_version', 'patient-pathway-history-draft-v1', ) || (string) ($lockedDraft->provenance['projection_method'] ?? '') !== self::DRAFT_PROJECTION_METHOD @@ -237,7 +237,7 @@ private function lockedDraftContext(PatientEncounterProjection $draft): array } $policy = PatientReleasePolicyVersion::query() ->effective() - ->where('version', (string) config('nightingale.policy_version')) + ->where('version', (string) config('hummingbird-patient.policy_version')) ->lockForUpdate() ->first(); if (! $policy instanceof PatientReleasePolicyVersion @@ -275,9 +275,9 @@ private function assertReleaseManager(User $manager): void private function assertAvailable(): void { if (DB::getDriverName() !== 'pgsql' - || ! (bool) config('nightingale.enabled') - || ! (bool) config('nightingale.features.pathway') - || ! (bool) config('nightingale.features.pathway_history_releases') + || ! (bool) config('hummingbird-patient.enabled') + || ! (bool) config('hummingbird-patient.features.pathway') + || ! (bool) config('hummingbird-patient.features.pathway_history_releases') || ! (bool) config('care-pathways.patient_enabled') || ! (bool) config('care-pathways.assignment_enabled')) { throw new RuntimeException('patient_pathway_history_releases_unavailable'); diff --git a/app/Services/Patient/Projection/PatientProjectionDisclosureService.php b/app/Services/Patient/Projection/PatientProjectionDisclosureService.php index 5a1ae4c6..093ba809 100644 --- a/app/Services/Patient/Projection/PatientProjectionDisclosureService.php +++ b/app/Services/Patient/Projection/PatientProjectionDisclosureService.php @@ -79,7 +79,7 @@ private function discloseWithinTransaction( $policy = PatientReleasePolicyVersion::query() ->effective() - ->where('version', (string) config('nightingale.policy_version')) + ->where('version', (string) config('hummingbird-patient.policy_version')) ->first(); if ($policy === null) { diff --git a/app/Services/Rounds/PatientRoundQuestionPromotionService.php b/app/Services/Rounds/PatientRoundQuestionPromotionService.php index 9505601f..272b1399 100644 --- a/app/Services/Rounds/PatientRoundQuestionPromotionService.php +++ b/app/Services/Rounds/PatientRoundQuestionPromotionService.php @@ -670,10 +670,10 @@ private function assertEnabled(): void private function isEnabled(): bool { return (bool) config('rounds.patient_question_bridge_enabled') - && (bool) config('nightingale.enabled') - && (bool) config('nightingale.features.messaging') - && (bool) config('nightingale.features.rounds_questions') - && (bool) config('nightingale.staff_messaging.enabled') - && config('nightingale.staff_messaging.governance_status') === 'approved'; + && (bool) config('hummingbird-patient.enabled') + && (bool) config('hummingbird-patient.features.messaging') + && (bool) config('hummingbird-patient.features.rounds_questions') + && (bool) config('hummingbird-patient.staff_messaging.enabled') + && config('hummingbird-patient.staff_messaging.governance_status') === 'approved'; } } diff --git a/bootstrap/app.php b/bootstrap/app.php index 51e65dc6..f4cfcc0a 100644 --- a/bootstrap/app.php +++ b/bootstrap/app.php @@ -4,8 +4,8 @@ use App\Http\Middleware\AuditUserRequests; use App\Http\Middleware\EnforceApiIngressContract; use App\Http\Middleware\EnsureClinicalFailureOutputSafe; -use App\Http\Middleware\EnsureNightingaleEnabled; -use App\Http\Middleware\EnsureNightingaleFeatureEnabled; +use App\Http\Middleware\EnsureHummingbirdPatientEnabled; +use App\Http\Middleware\EnsureHummingbirdPatientFeatureEnabled; use App\Http\Middleware\EnsurePatientRealm; use App\Http\Middleware\EnsurePatientStaffMessagingEnabled; use App\Http\Middleware\EnsureSessionIsCurrent; @@ -54,8 +54,8 @@ ->withMiddleware(function (Middleware $middleware) { $middleware->alias([ 'admin.scope' => RequireAdminScope::class, - 'patient.enabled' => EnsureNightingaleEnabled::class, - 'patient.feature' => EnsureNightingaleFeatureEnabled::class, + 'patient.enabled' => EnsureHummingbirdPatientEnabled::class, + 'patient.feature' => EnsureHummingbirdPatientFeatureEnabled::class, 'patient.realm' => EnsurePatientRealm::class, 'patient.response' => ProtectPatientResponse::class, 'patient.staff-messaging' => EnsurePatientStaffMessagingEnabled::class, @@ -67,14 +67,14 @@ // from absent routes. The response guard wraps both gates and auth errors. $middleware->prependToPriorityList( AuthenticatesRequests::class, - EnsureNightingaleFeatureEnabled::class, + EnsureHummingbirdPatientFeatureEnabled::class, ); $middleware->prependToPriorityList( - EnsureNightingaleFeatureEnabled::class, - EnsureNightingaleEnabled::class, + EnsureHummingbirdPatientFeatureEnabled::class, + EnsureHummingbirdPatientEnabled::class, ); $middleware->prependToPriorityList( - EnsureNightingaleEnabled::class, + EnsureHummingbirdPatientEnabled::class, ProtectPatientResponse::class, ); // The staff session-inventory guard must wrap Sanctum and every later diff --git a/config/hummingbird-patient.php b/config/hummingbird-patient.php new file mode 100644 index 00000000..a6e2dd79 --- /dev/null +++ b/config/hummingbird-patient.php @@ -0,0 +1,271 @@ + (bool) env('HUMMINGBIRD_PATIENT_ENABLED', false), + + // Dedicated domain key for patient-realm lookup and audit digests. This + // must not reuse APP_KEY. PatientHmac supplies a deterministic fallback in + // APP_ENV=testing only and fails closed everywhere else. + 'hmac_secret' => env('HUMMINGBIRD_PATIENT_HMAC_SECRET'), + + 'policy_version' => env('HUMMINGBIRD_PATIENT_POLICY_VERSION', 'patient-disclosure-v1-draft'), + + 'features' => [ + 'enrollment' => (bool) env('HUMMINGBIRD_PATIENT_ENROLLMENT_ENABLED', false), + 'token_exchange' => (bool) env('HUMMINGBIRD_PATIENT_TOKEN_EXCHANGE_ENABLED', false), + 'profile' => (bool) env('HUMMINGBIRD_PATIENT_PROFILE_ENABLED', false), + 'session_management' => (bool) env('HUMMINGBIRD_PATIENT_SESSION_MANAGEMENT_ENABLED', false), + // Registers an encrypted, revocable provider token only. This does + // not enable a notification provider, payload delivery, or push. + 'notification_devices' => (bool) env('HUMMINGBIRD_PATIENT_NOTIFICATION_DEVICES_ENABLED', false), + 'encounters' => (bool) env('HUMMINGBIRD_PATIENT_ENCOUNTERS_ENABLED', false), + 'today' => (bool) env('HUMMINGBIRD_PATIENT_TODAY_ENABLED', false), + 'pathway' => (bool) env('HUMMINGBIRD_PATIENT_PATHWAY_ENABLED', false), + // Produces a governed *draft* My Path projection from already + // version-pinned, append-only pathway history. This cannot release a + // projection or substitute for an approved production source adapter. + 'pathway_history_drafts' => (bool) env('HUMMINGBIRD_PATIENT_PATHWAY_HISTORY_DRAFTS_ENABLED', false), + // Admits only an allowlisted, connector-internal pathway status + // snapshot. It appends history only and cannot release a projection. + 'pathway_source_reconciliation' => (bool) env('HUMMINGBIRD_PATIENT_PATHWAY_SOURCE_RECONCILIATION_ENABLED', false), + // Requires a separate clinical review and a different catalog release + // manager before one pathway draft can become patient-visible. + 'pathway_history_releases' => (bool) env('HUMMINGBIRD_PATIENT_PATHWAY_HISTORY_RELEASES_ENABLED', false), + // Allows a patient to request clarification about an education item + // that is already present in their currently released pathway. This + // creates an accountable message only; it never records completion, + // comprehension, consent, or a clinician assessment. + 'teach_back' => (bool) env('HUMMINGBIRD_PATIENT_TEACH_BACK_ENABLED', false), + // Persists a content-free, patient-authored care-preference association + // beside the existing encrypted accountable message. It never writes + // or amends a clinical care plan, order, consent, or assessment. + 'care_preferences' => (bool) env('HUMMINGBIRD_PATIENT_CARE_PREFERENCES_ENABLED', false), + // Persists a content-free patient-authored personal-goal association + // beside its encrypted accountable message. It is never a clinical + // goal, care-plan change, order, consent, or assessment. + 'patient_goals' => (bool) env('HUMMINGBIRD_PATIENT_GOALS_ENABLED', false), + // Allows only the explicitly approved `rounds_question` messaging + // topic. It does not grant access to the staff virtual-rounds API. + 'rounds_questions' => (bool) env('HUMMINGBIRD_PATIENT_ROUNDS_QUESTIONS_ENABLED', false), + // A released, plain-language patient summary of rounds. This never + // enables or proxies the staff virtual-rounds workspace. + 'rounds_summary' => (bool) env('HUMMINGBIRD_PATIENT_ROUNDS_SUMMARY_ENABLED', false), + 'care_team' => (bool) env('HUMMINGBIRD_PATIENT_CARE_TEAM_ENABLED', false), + 'messaging' => (bool) env('HUMMINGBIRD_PATIENT_MESSAGING_ENABLED', false), + ], + + 'token' => [ + 'access_ttl_minutes' => max(1, (int) env('HUMMINGBIRD_PATIENT_ACCESS_TTL_MINUTES', 15)), + 'refresh_ttl_days' => max(1, (int) env('HUMMINGBIRD_PATIENT_REFRESH_TTL_DAYS', 14)), + ], + + 'enrollment' => [ + 'max_attempts' => max(1, (int) env('HUMMINGBIRD_PATIENT_ENROLLMENT_MAX_ATTEMPTS', 5)), + ], + + 'notification_devices' => [ + // This key ring is deliberately independent of APP_KEY and messaging + // body encryption. The registered provider token is never included in + // API responses, audit metadata, logs, or notification payloads. + 'encryption_key_version' => env('HUMMINGBIRD_PATIENT_NOTIFICATION_DEVICE_ENCRYPTION_KEY_VERSION'), + 'encryption_key' => env('HUMMINGBIRD_PATIENT_NOTIFICATION_DEVICE_ENCRYPTION_KEY'), + 'previous_encryption_keys_json' => env( + 'HUMMINGBIRD_PATIENT_NOTIFICATION_DEVICE_PREVIOUS_ENCRYPTION_KEYS_JSON', + ), + ], + + 'reference_provisioning' => [ + // Fail closed. Enable only in the deployed application runtime that + // owns the configured APP_KEY and dedicated patient HMAC secret. + 'enabled' => (bool) env('HUMMINGBIRD_PATIENT_REFERENCE_PROVISIONING_ENABLED', false), + 'encryption_key_version' => env('HUMMINGBIRD_PATIENT_REFERENCE_ENCRYPTION_KEY_VERSION'), + 'challenge_ttl_minutes' => max(5, min(30, (int) env( + 'HUMMINGBIRD_PATIENT_REFERENCE_CHALLENGE_TTL_MINUTES', + 10, + ))), + ], + + 'pathway_history_drafts' => [ + // These values deliberately describe the producer, not a source + // identifier. They are used only in protected cursor/provenance facts. + 'source_system_key' => 'care-pathways.pathway-history-v1', + 'producer_version' => 'patient-pathway-history-draft-v1', + 'current_after_minutes' => max(1, (int) env( + 'HUMMINGBIRD_PATIENT_PATHWAY_HISTORY_CURRENT_AFTER_MINUTES', + 30, + )), + 'stale_after_minutes' => max(2, (int) env( + 'HUMMINGBIRD_PATIENT_PATHWAY_HISTORY_STALE_AFTER_MINUTES', + 240, + )), + ], + + 'pathway_source_reconciliation' => [ + // No source is approved by default. Add a versioned key only through + // reviewed deployment code after clinical, privacy, and integration + // owners have approved its source contract. Never make this an env + // list: an arbitrary environment value must not authorize a source. + 'approved_sources' => [], + ], + + 'pathway_history_releases' => [ + 'producer_version' => 'patient-pathway-history-clinical-release-v1', + ], + + /* + |-------------------------------------------------------------------------- + | Patient-to-care-team messaging safety gate + |-------------------------------------------------------------------------- + | + | The feature flag is necessary but not sufficient. The registry refuses + | service unless this separate policy is explicitly approved and every + | patient-visible safety/response string is populated. Candidate topics + | are code-owned to prevent an arbitrary environment value from becoming + | an unreviewed routing destination. + | + */ + + 'messaging' => [ + 'governance_status' => env( + 'HUMMINGBIRD_PATIENT_MESSAGING_POLICY_STATUS', + 'draft_requires_approval', + ), + 'policy_version' => env('HUMMINGBIRD_PATIENT_MESSAGING_POLICY_VERSION'), + 'urgent_guidance_version' => env('HUMMINGBIRD_PATIENT_MESSAGING_URGENT_GUIDANCE_VERSION'), + 'urgent_guidance_text' => env('HUMMINGBIRD_PATIENT_MESSAGING_URGENT_GUIDANCE_TEXT'), + 'default_response_window' => env('HUMMINGBIRD_PATIENT_MESSAGING_DEFAULT_RESPONSE_WINDOW'), + 'encryption_key_version' => env('HUMMINGBIRD_PATIENT_MESSAGING_ENCRYPTION_KEY_VERSION'), + 'encryption_key' => env('HUMMINGBIRD_PATIENT_MESSAGING_ENCRYPTION_KEY'), + 'previous_encryption_keys_json' => env( + 'HUMMINGBIRD_PATIENT_MESSAGING_PREVIOUS_ENCRYPTION_KEYS_JSON', + ), + 'handoff_consumer' => env( + 'HUMMINGBIRD_PATIENT_MESSAGING_HANDOFF_CONSUMER', + DatabasePatientMessageHandoffConsumer::class, + ), + 'topics' => [ + 'rounds_question' => [ + 'label' => 'Question for care-team rounds', + 'description' => 'Share a non-urgent question your care team may review before a care conversation. Sending it does not promise it will be discussed in a particular round.', + 'responsibility_pool_key' => 'encounter.care_coordination', + ], + 'nursing_need' => [ + 'label' => 'Nursing question or need', + 'description' => 'Ask the nursing team about non-urgent care needs during this stay.', + 'responsibility_pool_key' => 'encounter.nursing', + ], + 'medication_question' => [ + 'label' => 'Medication question', + 'description' => 'Ask the care team a non-urgent question about your medicines.', + 'responsibility_pool_key' => 'encounter.medication', + ], + 'test_or_procedure' => [ + 'label' => 'Test or procedure question', + 'description' => 'Ask about preparation or next steps for a test or procedure.', + 'responsibility_pool_key' => 'encounter.tests_procedures', + ], + 'discharge_planning' => [ + 'label' => 'Discharge planning', + 'description' => 'Ask about preparing to leave the hospital and what comes next.', + 'responsibility_pool_key' => 'encounter.discharge_planning', + ], + 'therapy_or_mobility' => [ + 'label' => 'Therapy or mobility', + 'description' => 'Ask a non-urgent question about therapy, movement, or mobility.', + 'responsibility_pool_key' => 'encounter.therapy_mobility', + ], + 'nutrition' => [ + 'label' => 'Nutrition', + 'description' => 'Ask a non-urgent question about meals or nutrition during this stay.', + 'responsibility_pool_key' => 'encounter.nutrition', + ], + 'interpreter' => [ + 'label' => 'Interpreter or language help', + 'description' => 'Ask the care team for approved language or communication support.', + 'responsibility_pool_key' => 'encounter.language_access', + ], + 'care_coordination' => [ + 'label' => 'Care coordination', + 'description' => 'Ask a non-urgent question about how your care is being coordinated.', + 'responsibility_pool_key' => 'encounter.care_coordination', + ], + // This captures a patient-authored preference as an + // encrypted, accountable message. It deliberately does not + // create or amend a clinical care-plan record or order. + 'care_preference' => [ + 'label' => 'What matters to you', + 'description' => 'Share a non-urgent preference for this hospital stay. Your care team can review it, but it does not change your care plan or create a clinical order on its own.', + 'responsibility_pool_key' => 'encounter.care_coordination', + ], + 'patient_goal' => [ + 'label' => 'A personal goal for my stay', + 'description' => 'Share a non-urgent personal goal for this hospital stay. Your care team can review it, but it does not change your care plan or create a clinical order on its own.', + 'responsibility_pool_key' => 'encounter.care_coordination', + ], + // This topic is intentionally not listed in the general composer + // and cannot be used through the ordinary thread endpoint. The + // dedicated clarification endpoint verifies that its opaque item + // UUID belongs to a currently released pathway education item. + 'education_clarification' => [ + 'label' => 'Question about care information', + 'description' => 'Ask the care team to explain information shown in your care pathway. Sending a question does not confirm you understand it or complete an education task.', + 'responsibility_pool_key' => 'encounter.care_coordination', + 'composition_mode' => 'released_education_only', + ], + 'technical_help' => [ + 'label' => 'App help', + 'description' => 'Ask for help using Hummingbird Patient.', + 'responsibility_pool_key' => 'patient_app.technical_support', + ], + ], + ], + + /* + |-------------------------------------------------------------------------- + | Accountable staff-message handoff + |-------------------------------------------------------------------------- + | + | This is an independent kill switch and governance gate. Patient compose + | remains unavailable until every approved pilot unit has an effective + | topic pool, an eligible responder, and a recently healthy consumer. + | + */ + + 'staff_messaging' => [ + 'enabled' => (bool) env('HUMMINGBIRD_PATIENT_STAFF_MESSAGING_ENABLED', false), + 'governance_status' => env( + 'HUMMINGBIRD_PATIENT_STAFF_MESSAGING_POLICY_STATUS', + 'draft_requires_approval', + ), + 'consumer_key' => 'patient-message-staff-inbox-v1', + 'pilot_unit_ids' => array_values(array_filter( + array_map( + static fn (string $value): int => (int) trim($value), + explode(',', (string) env('HUMMINGBIRD_PATIENT_STAFF_MESSAGING_PILOT_UNIT_IDS', '')), + ), + static fn (int $value): bool => $value > 0, + )), + 'heartbeat_ttl_seconds' => max( + 30, + min(600, (int) env('HUMMINGBIRD_PATIENT_STAFF_MESSAGING_HEARTBEAT_TTL_SECONDS', 120)), + ), + 'batch_size' => max( + 1, + min(500, (int) env('HUMMINGBIRD_PATIENT_STAFF_MESSAGING_BATCH_SIZE', 100)), + ), + ], +]; diff --git a/config/hummingbird.php b/config/hummingbird.php index 1884a54c..84ed11f8 100644 --- a/config/hummingbird.php +++ b/config/hummingbird.php @@ -26,10 +26,10 @@ // APP_KEY does not silently become the patient-reference policy. 'patient_context' => [ 'signing_key' => env( - 'NIGHTINGALE_CONTEXT_KEY', + 'HUMMINGBIRD_PATIENT_CONTEXT_KEY', env('APP_ENV', 'production') === 'production' ? null : env('APP_KEY'), ), - 'ttl_minutes' => max(1, min(1440, (int) env('NIGHTINGALE_CONTEXT_TTL_MINUTES', 15))), + 'ttl_minutes' => max(1, min(1440, (int) env('HUMMINGBIRD_PATIENT_CONTEXT_TTL_MINUTES', 15))), ], // APNs (Apple Push Notification service), token-based (.p8) auth. When key_id + team_id + diff --git a/config/nightingale.php b/config/nightingale.php index 8f554791..98b3990d 100644 --- a/config/nightingale.php +++ b/config/nightingale.php @@ -1,271 +1,44 @@ (bool) env('NIGHTINGALE_ENABLED', false), - - // Dedicated domain key for patient-realm lookup and audit digests. This - // must not reuse APP_KEY. PatientHmac supplies a deterministic fallback in - // APP_ENV=testing only and fails closed everywhere else. - 'hmac_secret' => env('NIGHTINGALE_HMAC_SECRET'), - - 'policy_version' => env('NIGHTINGALE_POLICY_VERSION', 'patient-disclosure-v1-draft'), - - 'features' => [ - 'enrollment' => (bool) env('NIGHTINGALE_ENROLLMENT_ENABLED', false), - 'token_exchange' => (bool) env('NIGHTINGALE_TOKEN_EXCHANGE_ENABLED', false), - 'profile' => (bool) env('NIGHTINGALE_PROFILE_ENABLED', false), - 'session_management' => (bool) env('NIGHTINGALE_SESSION_MANAGEMENT_ENABLED', false), - // Registers an encrypted, revocable provider token only. This does - // not enable a notification provider, payload delivery, or push. - 'notification_devices' => (bool) env('NIGHTINGALE_NOTIFICATION_DEVICES_ENABLED', false), - 'encounters' => (bool) env('NIGHTINGALE_ENCOUNTERS_ENABLED', false), - 'today' => (bool) env('NIGHTINGALE_TODAY_ENABLED', false), - 'pathway' => (bool) env('NIGHTINGALE_PATHWAY_ENABLED', false), - // Produces a governed *draft* My Path projection from already - // version-pinned, append-only pathway history. This cannot release a - // projection or substitute for an approved production source adapter. - 'pathway_history_drafts' => (bool) env('NIGHTINGALE_PATHWAY_HISTORY_DRAFTS_ENABLED', false), - // Admits only an allowlisted, connector-internal pathway status - // snapshot. It appends history only and cannot release a projection. - 'pathway_source_reconciliation' => (bool) env('NIGHTINGALE_PATHWAY_SOURCE_RECONCILIATION_ENABLED', false), - // Requires a separate clinical review and a different catalog release - // manager before one pathway draft can become patient-visible. - 'pathway_history_releases' => (bool) env('NIGHTINGALE_PATHWAY_HISTORY_RELEASES_ENABLED', false), - // Allows a patient to request clarification about an education item - // that is already present in their currently released pathway. This - // creates an accountable message only; it never records completion, - // comprehension, consent, or a clinician assessment. - 'teach_back' => (bool) env('NIGHTINGALE_TEACH_BACK_ENABLED', false), - // Persists a content-free, patient-authored care-preference association - // beside the existing encrypted accountable message. It never writes - // or amends a clinical care plan, order, consent, or assessment. - 'care_preferences' => (bool) env('NIGHTINGALE_CARE_PREFERENCES_ENABLED', false), - // Persists a content-free patient-authored personal-goal association - // beside its encrypted accountable message. It is never a clinical - // goal, care-plan change, order, consent, or assessment. - 'patient_goals' => (bool) env('NIGHTINGALE_GOALS_ENABLED', false), - // Allows only the explicitly approved `rounds_question` messaging - // topic. It does not grant access to the staff virtual-rounds API. - 'rounds_questions' => (bool) env('NIGHTINGALE_ROUNDS_QUESTIONS_ENABLED', false), - // A released, plain-language patient summary of rounds. This never - // enables or proxies the staff virtual-rounds workspace. - 'rounds_summary' => (bool) env('NIGHTINGALE_ROUNDS_SUMMARY_ENABLED', false), - 'care_team' => (bool) env('NIGHTINGALE_CARE_TEAM_ENABLED', false), - 'messaging' => (bool) env('NIGHTINGALE_MESSAGING_ENABLED', false), - ], - - 'token' => [ - 'access_ttl_minutes' => max(1, (int) env('NIGHTINGALE_ACCESS_TTL_MINUTES', 15)), - 'refresh_ttl_days' => max(1, (int) env('NIGHTINGALE_REFRESH_TTL_DAYS', 14)), - ], - - 'enrollment' => [ - 'max_attempts' => max(1, (int) env('NIGHTINGALE_ENROLLMENT_MAX_ATTEMPTS', 5)), - ], - - 'notification_devices' => [ - // This key ring is deliberately independent of APP_KEY and messaging - // body encryption. The registered provider token is never included in - // API responses, audit metadata, logs, or notification payloads. - 'encryption_key_version' => env('NIGHTINGALE_NOTIFICATION_DEVICE_ENCRYPTION_KEY_VERSION'), - 'encryption_key' => env('NIGHTINGALE_NOTIFICATION_DEVICE_ENCRYPTION_KEY'), - 'previous_encryption_keys_json' => env( - 'NIGHTINGALE_NOTIFICATION_DEVICE_PREVIOUS_ENCRYPTION_KEYS_JSON', - ), - ], - - 'reference_provisioning' => [ - // Fail closed. Enable only in the deployed application runtime that - // owns the configured APP_KEY and dedicated patient HMAC secret. - 'enabled' => (bool) env('NIGHTINGALE_REFERENCE_PROVISIONING_ENABLED', false), - 'encryption_key_version' => env('NIGHTINGALE_REFERENCE_ENCRYPTION_KEY_VERSION'), - 'challenge_ttl_minutes' => max(5, min(30, (int) env( - 'NIGHTINGALE_REFERENCE_CHALLENGE_TTL_MINUTES', - 10, - ))), - ], - - 'pathway_history_drafts' => [ - // These values deliberately describe the producer, not a source - // identifier. They are used only in protected cursor/provenance facts. - 'source_system_key' => 'care-pathways.pathway-history-v1', - 'producer_version' => 'patient-pathway-history-draft-v1', - 'current_after_minutes' => max(1, (int) env( - 'NIGHTINGALE_PATHWAY_HISTORY_CURRENT_AFTER_MINUTES', - 30, - )), - 'stale_after_minutes' => max(2, (int) env( - 'NIGHTINGALE_PATHWAY_HISTORY_STALE_AFTER_MINUTES', - 240, - )), - ], - - 'pathway_source_reconciliation' => [ - // No source is approved by default. Add a versioned key only through - // reviewed deployment code after clinical, privacy, and integration - // owners have approved its source contract. Never make this an env - // list: an arbitrary environment value must not authorize a source. - 'approved_sources' => [], - ], - - 'pathway_history_releases' => [ - 'producer_version' => 'patient-pathway-history-clinical-release-v1', - ], - - /* - |-------------------------------------------------------------------------- - | Patient-to-care-team messaging safety gate - |-------------------------------------------------------------------------- - | - | The feature flag is necessary but not sufficient. The registry refuses - | service unless this separate policy is explicitly approved and every - | patient-visible safety/response string is populated. Candidate topics - | are code-owned to prevent an arbitrary environment value from becoming - | an unreviewed routing destination. - | - */ - - 'messaging' => [ - 'governance_status' => env( - 'NIGHTINGALE_MESSAGING_POLICY_STATUS', - 'draft_requires_approval', - ), - 'policy_version' => env('NIGHTINGALE_MESSAGING_POLICY_VERSION'), - 'urgent_guidance_version' => env('NIGHTINGALE_MESSAGING_URGENT_GUIDANCE_VERSION'), - 'urgent_guidance_text' => env('NIGHTINGALE_MESSAGING_URGENT_GUIDANCE_TEXT'), - 'default_response_window' => env('NIGHTINGALE_MESSAGING_DEFAULT_RESPONSE_WINDOW'), - 'encryption_key_version' => env('NIGHTINGALE_MESSAGING_ENCRYPTION_KEY_VERSION'), - 'encryption_key' => env('NIGHTINGALE_MESSAGING_ENCRYPTION_KEY'), - 'previous_encryption_keys_json' => env( - 'NIGHTINGALE_MESSAGING_PREVIOUS_ENCRYPTION_KEYS_JSON', - ), - 'handoff_consumer' => env( - 'NIGHTINGALE_MESSAGING_HANDOFF_CONSUMER', - DatabasePatientMessageHandoffConsumer::class, - ), - 'topics' => [ - 'rounds_question' => [ - 'label' => 'Question for care-team rounds', - 'description' => 'Share a non-urgent question your care team may review before a care conversation. Sending it does not promise it will be discussed in a particular round.', - 'responsibility_pool_key' => 'encounter.care_coordination', - ], - 'nursing_need' => [ - 'label' => 'Nursing question or need', - 'description' => 'Ask the nursing team about non-urgent care needs during this stay.', - 'responsibility_pool_key' => 'encounter.nursing', - ], - 'medication_question' => [ - 'label' => 'Medication question', - 'description' => 'Ask the care team a non-urgent question about your medicines.', - 'responsibility_pool_key' => 'encounter.medication', - ], - 'test_or_procedure' => [ - 'label' => 'Test or procedure question', - 'description' => 'Ask about preparation or next steps for a test or procedure.', - 'responsibility_pool_key' => 'encounter.tests_procedures', - ], - 'discharge_planning' => [ - 'label' => 'Discharge planning', - 'description' => 'Ask about preparing to leave the hospital and what comes next.', - 'responsibility_pool_key' => 'encounter.discharge_planning', - ], - 'therapy_or_mobility' => [ - 'label' => 'Therapy or mobility', - 'description' => 'Ask a non-urgent question about therapy, movement, or mobility.', - 'responsibility_pool_key' => 'encounter.therapy_mobility', - ], - 'nutrition' => [ - 'label' => 'Nutrition', - 'description' => 'Ask a non-urgent question about meals or nutrition during this stay.', - 'responsibility_pool_key' => 'encounter.nutrition', - ], - 'interpreter' => [ - 'label' => 'Interpreter or language help', - 'description' => 'Ask the care team for approved language or communication support.', - 'responsibility_pool_key' => 'encounter.language_access', - ], - 'care_coordination' => [ - 'label' => 'Care coordination', - 'description' => 'Ask a non-urgent question about how your care is being coordinated.', - 'responsibility_pool_key' => 'encounter.care_coordination', - ], - // This captures a patient-authored preference as an - // encrypted, accountable message. It deliberately does not - // create or amend a clinical care-plan record or order. - 'care_preference' => [ - 'label' => 'What matters to you', - 'description' => 'Share a non-urgent preference for this hospital stay. Your care team can review it, but it does not change your care plan or create a clinical order on its own.', - 'responsibility_pool_key' => 'encounter.care_coordination', - ], - 'patient_goal' => [ - 'label' => 'A personal goal for my stay', - 'description' => 'Share a non-urgent personal goal for this hospital stay. Your care team can review it, but it does not change your care plan or create a clinical order on its own.', - 'responsibility_pool_key' => 'encounter.care_coordination', - ], - // This topic is intentionally not listed in the general composer - // and cannot be used through the ordinary thread endpoint. The - // dedicated clarification endpoint verifies that its opaque item - // UUID belongs to a currently released pathway education item. - 'education_clarification' => [ - 'label' => 'Question about care information', - 'description' => 'Ask the care team to explain information shown in your care pathway. Sending a question does not confirm you understand it or complete an education task.', - 'responsibility_pool_key' => 'encounter.care_coordination', - 'composition_mode' => 'released_education_only', - ], - 'technical_help' => [ - 'label' => 'App help', - 'description' => 'Ask for help using Hummingbird Patient.', - 'responsibility_pool_key' => 'patient_app.technical_support', - ], - ], - ], - - /* - |-------------------------------------------------------------------------- - | Accountable staff-message handoff - |-------------------------------------------------------------------------- - | - | This is an independent kill switch and governance gate. Patient compose - | remains unavailable until every approved pilot unit has an effective - | topic pool, an eligible responder, and a recently healthy consumer. - | - */ - - 'staff_messaging' => [ - 'enabled' => (bool) env('NIGHTINGALE_STAFF_MESSAGING_ENABLED', false), - 'governance_status' => env( - 'NIGHTINGALE_STAFF_MESSAGING_POLICY_STATUS', - 'draft_requires_approval', - ), - 'consumer_key' => 'patient-message-staff-inbox-v1', - 'pilot_unit_ids' => array_values(array_filter( - array_map( - static fn (string $value): int => (int) trim($value), - explode(',', (string) env('NIGHTINGALE_STAFF_MESSAGING_PILOT_UNIT_IDS', '')), - ), - static fn (int $value): bool => $value > 0, - )), - 'heartbeat_ttl_seconds' => max( - 30, - min(600, (int) env('NIGHTINGALE_STAFF_MESSAGING_HEARTBEAT_TTL_SECONDS', 120)), - ), - 'batch_size' => max( - 1, - min(500, (int) env('NIGHTINGALE_STAFF_MESSAGING_BATCH_SIZE', 100)), - ), - ], + 'status' => 'foundation', + 'route_namespace' => '/api/nightingale/v1', + 'routes_registered' => false, + 'network_clients_permitted' => false, + 'identity' => [ + 'enabled' => false, + 'provider' => null, + 'legacy_patient_realm_reuse_permitted' => false, + ], + 'inpatient_context' => [ + 'enabled' => false, + 'candidate_path' => '/inpatient-contexts', + 'operation_id' => 'listNightingaleInpatientContexts', + 'source_adapter' => null, + 'production_query_permitted' => false, + ], + 'activation' => [ + 'clinical_approval_state' => 'absent', + 'clinical_approval_record' => null, + 'content_release_state' => 'unreleased', + 'content_release_id' => null, + 'feature_activation_state' => 'disabled', + 'pilot_enrollment_state' => 'not_enrolled', + 'source_connector_state' => 'undeployed', + ], + 'patient_disclosure_enabled' => false, + 'patient_mutation_enabled' => false, + 'production_enabled' => false, ]; diff --git a/docs/README.md b/docs/README.md index 7258708b..a48a8dfc 100644 --- a/docs/README.md +++ b/docs/README.md @@ -15,29 +15,30 @@ Everything else — including deployment runbooks and the business plan — file ## Map -| Directory | What belongs here | Start here | -| --- | --- | --- | -| [product/](./product/) | PRDs, master plans, roadmaps, scope, demo narrative | [ZEPHYRUS-2.0-PLAN.md](./product/ZEPHYRUS-2.0-PLAN.md), [ZEPHYRUS-2.0-BETA-PRD.md](./product/ZEPHYRUS-2.0-BETA-PRD.md) | -| [architecture/](./architecture/) | Durable system architecture and taxonomies — outlives any one initiative | [OCEL-DRG care-pathway measurement](./architecture/OCEL-DRG-CARE-PATHWAY-MEASUREMENT-ROOT-CAUSE-ARCHITECTURE-2026-07-21.md), [service-line/location taxonomy](./architecture/SERVICE-LINE-LOCATION-DEPLOYMENT-TAXONOMY-2026-07-04.md) | -| [plans/](./plans/) | Dated initiative and implementation plans, plus their companion evidence | [plans/zephyrus-2.0-beta/](./plans/zephyrus-2.0-beta/) (B0–B8 delivery program) | -| [devlog/](./devlog/) | Dated execution logs — what was actually built, verified, and merged | Newest first: [governed DRG care pathways](./devlog/DEVLOG-governed-drg-care-pathways-2026-07-22.md) | -| [audits/](./audits/) | Audits, reviews, and the instruments used to run them | [Comprehensive UX/UI + HFE audit](./audits/ZEPHYRUS_COMPREHENSIVE_UX_UI_HFE_AUDIT_2026-07-17.md) | -| [guides/](./guides/) | Engineering reference and how-to for contributors | [coding-standards.md](./guides/coding-standards.md) | -| [operations/](./operations/) | Runbooks for running, deploying, and releasing the system | [DEPLOY_NOW.md](./operations/DEPLOY_NOW.md) (manual deploy is the only supported path), [DEPLOYMENT_CHECKLIST.md](./operations/DEPLOYMENT_CHECKLIST.md), [DEVELOPMENT-AND-PRODUCTION-RELEASE-RUNBOOK.md](./operations/DEVELOPMENT-AND-PRODUCTION-RELEASE-RUNBOOK.md) | -| [evidence/](./evidence/) | Acceptance/verification artifacts (screenshots, query output, import logs) | [evidence/ancillary/](./evidence/ancillary/) | -| [hummingbird/](./hummingbird/) | Mobile companion app: ADRs, API contracts, design tokens, personas | [hummingbird/README.md](./hummingbird/README.md) | -| [home-hospital/](./home-hospital/) | Hospital-at-Home / RPM virtual-ward module | [HOME-HOSPITAL-BUILD-PROMPT.md](./home-hospital/HOME-HOSPITAL-BUILD-PROMPT.md) | -| [superpowers/](./superpowers/) | Skill-authored implementation plans + design specs (**stable path**, see below) | [superpowers/plans/](./superpowers/plans/) | -| [business/](./business/) | Business plan, investor and GTM material, deck build tooling | [BUSINESS_PLAN.md](./business/BUSINESS_PLAN.md), [Zephyrus_Investor_Deck_Reconciled.pdf](./business/Zephyrus_Investor_Deck_Reconciled.pdf) | -| [reference/](./reference/) | External prototypes and third-party inputs we read but do not own | [hospital-operations-cockpit/](./reference/hospital-operations-cockpit/) | -| [screenshots/](./screenshots/) | App screenshots used by the repo README | — | -| [archive/](./archive/) | Superseded documents, kept for provenance | [archive/README.md](./archive/README.md) | +| Directory | What belongs here | Start here | +| ---------------------------------- | ------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| [product/](./product/) | PRDs, master plans, roadmaps, scope, demo narrative | [ZEPHYRUS-2.0-PLAN.md](./product/ZEPHYRUS-2.0-PLAN.md), [ZEPHYRUS-2.0-BETA-PRD.md](./product/ZEPHYRUS-2.0-BETA-PRD.md) | +| [architecture/](./architecture/) | Durable system architecture and taxonomies — outlives any one initiative | [OCEL-DRG care-pathway measurement](./architecture/OCEL-DRG-CARE-PATHWAY-MEASUREMENT-ROOT-CAUSE-ARCHITECTURE-2026-07-21.md), [service-line/location taxonomy](./architecture/SERVICE-LINE-LOCATION-DEPLOYMENT-TAXONOMY-2026-07-04.md) | +| [plans/](./plans/) | Dated initiative and implementation plans, plus their companion evidence | [plans/zephyrus-2.0-beta/](./plans/zephyrus-2.0-beta/) (B0–B8 delivery program) | +| [devlog/](./devlog/) | Dated execution logs — what was actually built, verified, and merged | Newest first: [governed DRG care pathways](./devlog/DEVLOG-governed-drg-care-pathways-2026-07-22.md) | +| [audits/](./audits/) | Audits, reviews, and the instruments used to run them | [Comprehensive UX/UI + HFE audit](./audits/ZEPHYRUS_COMPREHENSIVE_UX_UI_HFE_AUDIT_2026-07-17.md) | +| [guides/](./guides/) | Engineering reference and how-to for contributors | [coding-standards.md](./guides/coding-standards.md) | +| [operations/](./operations/) | Runbooks for running, deploying, and releasing the system | [DEPLOY_NOW.md](./operations/DEPLOY_NOW.md) (manual deploy is the only supported path), [DEPLOYMENT_CHECKLIST.md](./operations/DEPLOYMENT_CHECKLIST.md), [DEVELOPMENT-AND-PRODUCTION-RELEASE-RUNBOOK.md](./operations/DEVELOPMENT-AND-PRODUCTION-RELEASE-RUNBOOK.md) | +| [evidence/](./evidence/) | Acceptance/verification artifacts (screenshots, query output, import logs) | [evidence/ancillary/](./evidence/ancillary/) | +| [hummingbird/](./hummingbird/) | Mobile companion app: ADRs, API contracts, design tokens, personas | [hummingbird/README.md](./hummingbird/README.md) | +| [nightingale/](./nightingale/) | Independent inpatient-facing product contracts, safety evidence, namespace, and migration lineage | [nightingale/README.md](./nightingale/README.md), [namespace and activation separation](./nightingale/NAMESPACE-AND-ACTIVATION-SEPARATION-FOUNDATION-2026-07-27.md), [controlled-pilot manifest](./nightingale/CONTROLLED-PILOT-MANIFEST-FOUNDATION-2026-07-27.md) | +| [home-hospital/](./home-hospital/) | Hospital-at-Home / RPM virtual-ward module | [HOME-HOSPITAL-BUILD-PROMPT.md](./home-hospital/HOME-HOSPITAL-BUILD-PROMPT.md) | +| [superpowers/](./superpowers/) | Skill-authored implementation plans + design specs (**stable path**, see below) | [superpowers/plans/](./superpowers/plans/) | +| [business/](./business/) | Business plan, investor and GTM material, deck build tooling | [BUSINESS_PLAN.md](./business/BUSINESS_PLAN.md), [Zephyrus_Investor_Deck_Reconciled.pdf](./business/Zephyrus_Investor_Deck_Reconciled.pdf) | +| [reference/](./reference/) | External prototypes and third-party inputs we read but do not own | [hospital-operations-cockpit/](./reference/hospital-operations-cockpit/) | +| [screenshots/](./screenshots/) | App screenshots used by the repo README | — | +| [archive/](./archive/) | Superseded documents, kept for provenance | [archive/README.md](./archive/README.md) | ## Filing rules 1. **Kind first.** A new Flow-4D plan goes in `plans/`, not a `flow-4d/` folder. Only a module that ships its own contracts or app code earns a per-surface folder - (`hummingbird/`, `home-hospital/`). + (`hummingbird/`, `nightingale/`, `home-hospital/`). 2. **Plans and devlogs pair up.** An initiative plan in `plans/` gets its execution log in `devlog/` under the same slug; cross-link them at the top of both files. 3. **Date anything time-bound.** `-YYYY-MM-DD` suffix for plans, audits, and devlogs; no diff --git a/docs/devlog/DEVLOG-nightingale-patient-product-2026-07-26.md b/docs/devlog/DEVLOG-nightingale-patient-product-2026-07-26.md new file mode 100644 index 00000000..ddf36aff --- /dev/null +++ b/docs/devlog/DEVLOG-nightingale-patient-product-2026-07-26.md @@ -0,0 +1,2392 @@ +# Nightingale Patient Product — Execution Log + +**Initiative plan:** +[nightingale-patient-product-2026-07-26.md](../plans/nightingale-patient-product-2026-07-26.md) + +## 2026-07-26 — Product direction and isolated development stream + +### Decision record + +Nightingale is the new dedicated patient product. Hummingbird is now the staff +operations product only. This is an architecture and product-identity decision, not a +clinical, patient-identity, content-release, pilot, database, migration, or production +deployment authorization. + +### Completed evidence + +- Created branch `codex/nightingale-patient-product` in a clean worktree at + `/Users/sudoshi/Github/Zephyrus-nightingale-patient`, based on `origin/main` commit + `446107ec`. +- Inspected the current independent staff targets (`net.acumenus.hummingbird`) and the + legacy patient-reference targets (`net.acumenus.hummingbird.patient`) on both iOS and + Android. The latter remain preserved as reference evidence and are not the Nightingale + release target. +- Recorded supplied brand sources as RGBA 1254 × 1254 PNGs and pinned their SHA-256 + fingerprints in the initiative plan and product-specific provenance manifests. +- Added `scripts/brand/render-app-icon.swift`, a deterministic AppKit renderer. It created + opaque iOS masters and Android density launcher derivatives, plus transparent Android + adaptive-icon foregrounds with an 8% safe-zone inset. Hummingbird uses `#050B12` and + Nightingale uses `#17120E` as reviewed opaque launcher backgrounds. +- Replaced the Hummingbird Staff iOS AppIcon/brand mark and Android + launcher/round/foreground derivatives with the supplied hummingbird source. No legacy + Hummingbird Patient asset or target was changed. +- Created the Nightingale iOS and Android roots, each with its own + `net.acumenus.nightingale` application identity, unit/UI smoke tests, and an intentionally + no-network, no-data foundation screen. The Android manifest has no `INTERNET` permission; + its Gradle boundary task rejects staff product/endpoints. The iOS and Android boundary + tests assert that live patient and staff endpoint access are disabled. +- Generated the Nightingale Xcode project from `project.yml`. The Nightingale iOS unit and + UI tests pass on the booted iPhone 17 Pro simulator. Android unit/boundary tests and the + API 35 (`hb`) emulator instrumentation smoke test pass. Android visual review captured + the Nightingale Android 12+ splash and foundation screen, and the Hummingbird Staff splash. +- Verified Hummingbird Staff iOS project-generation drift and a Simulator Debug build. Its + Android Debug APK builds, installs, and displays the supplied hummingbird mark on the + Android 12+ splash surface. +- Confirmed the existing patient reference already has a distinct API, storage, and + lifecycle boundary. That is useful migration input, not proof that the new Nightingale + product is ready or authorized. + +### Open work and holds + +- Both Nightingale native targets now compile in Debug and Release, and both Hummingbird Staff + targets compile in Debug and Release after the icon replacement. This does not satisfy + signing, distribution, rights, or clinical/patient release requirements. +- Actual iOS launcher-surface inspection, Android round/adaptive launcher inspection, Android + 13+ monochrome-icon design, app-store/signing/rights release steps, and all clinical/patient + authorization gates remain open. The current build and emulator evidence is only a + foundation verification, not distribution approval. +- The existing patient contract remains governed compatibility input until a Nightingale + contract/release migration is independently approved. +- No production patient, database record, session, grant, projection release, feature flag, + migration, deployment, or pilot action was taken. + +## 2026-07-26 — Branded privacy and accessibility foundation + +### Completed evidence + +- Classified the first seven legacy reference sources by safety primitive, product behavior, + test/fixture-only, or rejected behavior. The classification explicitly holds every unlisted + reference source and records why no patient API, credential, session, clinical projection, + messaging behavior, or synthetic patient was migrated. +- Added a Nightingale-only scenic foundation using the supplied nightingale artwork, warm/cool + low-contrast gradients, opaque content cards, decorative-image semantics, large-text image + attenuation, iOS high-contrast image withholding, a tested Android high-contrast policy seam, + and reduced-motion-aware iOS transitions. Android runtime high-contrast integration remains + held for an approved Nightingale presentation preference rather than a private platform API. +- Added lifecycle privacy covers on iOS and Android with Nightingale-specific copy and + accessibility identifiers. Foundation content is hidden from assistive interaction while + the iOS cover is visible. +- Added mandatory Android `FLAG_SECURE`. On API 35 the app remained fully represented in the + accessibility hierarchy while a system screenshot returned a black frame, confirming that + the capture control protects visual content without erasing assistive semantics. +- Added `verify-nightingale-product-boundary.sh`, which rejects staff/legacy identifiers, + staff endpoints, Android network permission, and native network-client symbols and verifies + the independent iOS/Android application identifiers. + +### Verification + +- iOS: XcodeGen project generation and the full Nightingale unit/UI suite pass on iPhone 17 Pro + Simulator. Both the normal foundation and forced privacy-cover states were visually reviewed. +- Android: unit/boundary tests pass; three API 35 instrumentation tests pass for launch copy, + secure-window enforcement, and lifecycle privacy-cover state. Accessibility-tree inspection + confirms the complete no-data patient-safe message. +- Boundary: no staff namespace, legacy patient package, staff endpoint, Android network + permission, or native URL client is present in Nightingale application sources. + +### Holds + +- This slice contains no patient identity, credential storage, API client, clinical content, + care-team communication, notification, analytics, production database access, feature + activation, migration, deployment, or pilot enrollment. +- Remaining legacy sources are not migration-approved. Clinical, privacy/security, + accessibility, patient-advisor, identity, legal/HIM, and release approvals remain open. + +## 2026-07-26 — Product identity and launcher evidence hardening + +### Completed evidence + +- Added independent Android 13+ monochrome adaptive resources for Hummingbird Staff and + Nightingale. The generated resources are white alpha silhouettes, not full-color + foregrounds mislabeled as monochrome artwork. +- Ran the first round-mask review on the Android API 35 emulator, detected that the + Hummingbird beak was clipped at the original 8% adaptive inset, corrected the + Hummingbird adaptive/monochrome inset to 20%, rebuilt, reinstalled, and repeated the + review. The corrected round and themed icons retain the complete subject. +- Captured non-PHI light/dark iOS launcher, Android round adaptive, Android light/dark + themed-icon, and Android system-splash evidence for both products in + [the brand evidence record](../evidence/nightingale/brand-identity-2026-07-26/README.md). +- Corrected the deterministic icon renderer so `opaque` outputs are RGB PNGs without alpha + channels. Regenerated both iOS AppIcon masters and all legacy Android launcher/round + density outputs. +- Added `verify-app-icon.swift` and `verify-mobile-brand-assets.sh`. They verify source + checksums, dimensions, alpha-channel policy, visible/transparent pixel presence, + monochrome pixel purity, Android v33 resource wiring, and cross-product distinction. +- Added the brand verifier as an independent macOS CI job so future non-documentation + changes cannot silently reintroduce alpha, source drift, cross-product identity, or a + malformed themed-icon resource. +- Added the + [product identity and support naming checklist](../nightingale/PRODUCT-IDENTITY-AND-SUPPORT-NAMING-CHECKLIST-2026-07-26.md). + It records canonical product names and app IDs while leaving all external reservations, + signing, public support contacts, distribution rights, store metadata, and approvals + explicitly pending. + +### Verification + +- Hummingbird Android Debug and Release builds accepted the corrected adaptive and + monochrome resources. +- Nightingale Android Debug and Release builds and its product-boundary task accepted the + independent resources. +- The brand-asset verifier confirms both iOS masters and legacy Android launchers have no + alpha channel, while adaptive and monochrome foregrounds retain required transparency. +- Nightingale’s iOS XCTest/XCUITest scheme passed three tests with zero failures; its + Android API 35 instrumentation suite passed three tests with zero failures. Hummingbird + and Nightingale Android unit suites passed, and current Debug/Release builds succeeded. +- No patient data, credential, production database, patient record, feature activation, + migration, deployment, or store-console mutation was used. + +### Holds + +- The remaining Stream B cross-surface audit covers notification, widget, installed + upgrade, and future store-listing surfaces. It is not inferred from launcher evidence. +- Artwork ownership/distribution rights and independent product-design/accessibility + review remain open. +- Apple/Google records, signing, support endpoints, privacy disclosures, analytics/crash + boundaries, push identities, and pilot/release authorization remain open. + +## 2026-07-26 — Pre-identity protected-state and volatile-input foundation + +### Decisions and classification + +- Added the + [identity, recovery, and protected-state decision record](../nightingale/IDENTITY-RECOVERY-AND-PROTECTED-STATE-DECISIONS-2026-07-26.md) + before adding storage code. It separates local deletion from remote revocation, prohibits + durable access tokens and device UUIDs, withholds a refresh-token decision, reserves one + future binding descriptor, defines recovery/account-transition clearing, and records + device-compromise and memory-zeroization limitations. +- Classified the legacy iOS and Android secure stores as mixed reference sources. Their + Hummingbird namespaces, access/refresh tokens, session/device UUIDs, and implicit + migration behavior were rejected. +- Classified legacy iOS/Android message composition as product behavior. The useful + no-durable-draft principle was retained, while legacy message UI, routing, clinical copy, + and send operations remain held. + +### Implemented foundation + +- Added a dormant iOS generic-password Keychain store using the exact Nightingale service, + the data-protection Keychain, `kSecAttrAccessibleWhenUnlockedThisDeviceOnly`, disabled + synchronization, explicit status errors, empty-value rejection, and idempotent + service-wide deletion. There is no access-token, refresh-token, device-identity, network, + or production caller. +- Added a dormant Android protected-state store using a non-exportable 256-bit AES + `AndroidKeyStore` key, AES-GCM with a fresh 12-byte IV, authenticated application/schema + context, a versioned ciphertext envelope, app-private storage, and explicit verified + deletion of both key and ciphertext. Unknown/corrupt/tampered state fails closed. +- Kept Android application backup disabled and added explicit cloud-backup/device-transfer + exclusion rules. Both native product-boundary checks now pin that posture. +- Added process-memory-only volatile-input state on both platforms. The active roots clear + it when the app becomes inactive; tests also pin logout, identity transition, recovery, + revocation, and local-removal reasons. The implementation explicitly does not claim + immutable-string memory zeroization. + +### Verification + +- iOS: the normally signed iPhone 17 Pro Simulator run passed five unit tests and two UI + tests. The real Keychain test round-tripped a synthetic canary, inspected the + `WhenUnlockedThisDeviceOnly` and non-synchronizing attributes, and verified idempotent + deletion. A deliberately unsigned diagnostic run produced `errSecMissingEntitlement` + (`-34018`), proving the test does not silently bypass a missing platform capability; the + signed run passed without weakening the query. +- Android: JVM tests, Debug assembly, AndroidTest assembly, and Release assembly pass. The + API 35 `hb` emulator passed five instrumentation tests, including real Keystore/GCM + round-trip, ciphertext-not-plaintext inspection, tamper rejection, deletion of key and + ciphertext, idempotent deletion, and lifecycle draft clearing. +- The Nightingale no-network/product-boundary scans remain green. No network permission, + API client, patient input UI, or legacy storage namespace was introduced. + +### Holds + +- The protected stores are platform foundations, not an approved credential design. Real + identity-provider selection, proofing, representative access, refresh/session policy, + user-presence policy, recovery, support, penetration testing, and pilot approval remain + open. +- No production database, patient, credential, identity record, grant, session, API, + feature flag, migration, deployment, or pilot state was read or changed. + +## 2026-07-26 — Empty contract governance and vocabulary reconciliation + +### Contract inventory and decisions + +- Reviewed the legacy Hummingbird Patient OpenAPI contract (23 paths and 25 operations), + Laravel patient route group, default-off configuration, middleware, policies, disclosure + services, projection guards, messaging services, principals, sessions, grants, releases, + devices, and communication models. +- Added the + [Nightingale contract ownership and authorization matrix](../nightingale/CONTRACT-OWNERSHIP-AND-AUTHORIZATION-MATRIX-2026-07-26.md). + It assigns accountable disciplines, defines version/namespace holds, specifies the full + authorization lattice, records response/mutation invariants, and gives every legacy + operation an explicit held disposition. +- Added `nightingale-foundation.v0.json`, an OpenAPI 3.1.1 governance artifact with version + `0.0.0-governance`, zero paths, zero webhooks, no components/security scheme, a reserved + `.invalid` placeholder server, no client-generation permission, no route reservation, + and every runtime/identity/disclosure/mutation/production activation field false. +- Added a dependency-free CI verifier and a dedicated docs-sensitive CI job. The verifier + rejects any path, webhook, component, security definition, usable server, activation, + legacy/staff route, production host, or Hummingbird identity entering the foundation. + +### Vocabulary and field reconciliation + +- Added the + [patient-state vocabulary classification](../nightingale/PATIENT-STATE-VOCABULARY-CLASSIFICATION-2026-07-26.md) + after reconciling the backend registry, backend content guard, OpenAPI schema, iOS + registry/models/rendering, and Android registry/models/rendering. +- Counted 12 backend domains and 49 code-label pairs, versus 8 domains/37 pairs in the iOS + versioned registry and 9 domains/41 pairs in Android. iOS implements four event-category + labels outside its versioned registry, bringing implemented label coverage to 41 without + equivalent governance. +- Found that `goal_author`, `location_status`, and `contact_route` are not centrally + versioned in either client; goal/contact labels are duplicated in rendering switches and + Android omits the Today `care_location` and `discharge_outlook` documents. +- Found a material schema-placement conflict: legacy OpenAPI allows category on schedule + items while backend/native schedule models do not; backend/native pathway events support + category while legacy OpenAPI does not define it there. +- Recorded materially different unknown-category behavior (closed-enum response failure on + iOS versus neutral string fallback on Android), the unsafe Nightingale implication of + treating a missing vocabulary version as compatible, and the backend class's lack of a + direct version-to-registry binding. + +### Verification and holds + +- The contract foundation verifier, its negative mutation self-tests, the native + no-network/product-boundary verifier, formatting checks, link checks, and Git whitespace + validation pass. +- The Nightingale native applications remain unchanged by this slice. A fresh signed + iPhone 17 Pro Simulator run passed five unit tests and two UI tests; the Android API 35 + `hb` emulator passed all five instrumentation tests, and Android JVM, Debug, and Release + tasks passed. The iOS Release Simulator build also passed. These runs reconfirm the + no-network/protected-state/lifecycle shell; no API or vocabulary runtime exists to + exercise on a device. +- No route, controller, security scheme, native client, network permission, credential, + patient input, vocabulary code/label, clinical projection, communication behavior, + production database, patient record, feature flag, migration, deployment, or pilot state + was created, read, or changed. +- The contract must remain empty until named owners approve the applicable pre-operation + gates. All vocabulary codes and labels remain held until one version/checksum-bound + Nightingale registry and canonical cross-platform fixtures are independently approved. + +## 2026-07-26 — First read-only encounter-access held candidate + +### Source scrutiny + +- Traced the legacy `GET /encounters` request from product/feature gates through Sanctum, + patient-realm/session/ability middleware, grant policies/query filters, per-row audit, + response metadata, OpenAPI schema, backend feature coverage, and both native decoders and + session coordinators. +- Recorded source hashes and the full analysis in the + [encounter-access candidate decision](../nightingale/ENCOUNTER-ACCESS-CANDIDATE-DECISION-2026-07-26.md). +- Identified that the legacy response exposes grant UUID, raw scopes, relationship, grant + validity dates, and row version even though the clients need only a navigation handle. +- Identified that both legacy apps silently choose the first encounter, while backend order + is an authorization-record sort rather than a patient-safe transfer/readmission rule. +- Identified missing list-time proof of verified/current identity-link ownership and current + inpatient source state; nullable effective-time drift across database/service/policy/ + contract/fixture; raw unvalidated scope arrays; row-oriented audit that omits an empty + evaluation event; and a non-coherent maximum-row collection version/freshness claim. +- Confirmed the principal backend feature test proves one active-row happy path and selected + source-field omission, but does not cover the full identity, status, time, source, + cardinality, data-integrity, audit, race, or cross-platform matrix. + +### Held candidate and fixtures + +- Added a non-runnable candidate artifact with null route and operation ID, no OpenAPI + inclusion, no namespace reservation, no client generation/network permission, and every + activation field false. +- Reduced the candidate success entry to one separately issued Nightingale opaque + `encounter_handle`; explicitly excluded 12 legacy/source/identity fields and any durable + native storage. +- Restricted the initial candidate to `self` and zero or one eligible inpatient context. + More than one returns a generic account-review result; a dependency outage returns + temporary unavailability rather than a false empty result. +- Defined 42 synthetic cases across success, complete omission, release gating, + authentication, account/session state, identity/source integrity, unknown registry data, + opaque-handle integrity, dependency/audit failure, cardinality, race, policy mismatch, + malformed scope registry, and throttling. +- Added eight exact response templates with bounded patient language, a one-field success + payload, exact no-store/privacy headers, policy/evaluation metadata, no links, and no + irrelevant state-vocabulary version. +- Added a dependency-free verifier that checks the held state, foundation zero-path state, + exact field/header/template/case/audit mappings, handle/request formats, synthetic-only + fixtures, production/legacy token absence, and all 42 case IDs. Five mutation self-tests + prove it rejects operation activation, a second encounter, a legacy grant field, a + weakened identity-link outcome, and production fixture replay. + +### Verification and holds + +- Both Nightingale contract verifiers (including negative self-tests), the native + no-network/product-boundary verifier, JSON/Markdown/YAML formatting, relative-link + checks, JavaScript syntax checks, and Git whitespace checks pass. +- An optional local `PatientApiBoundaryTest` corroboration was attempted against the + `phpunit.xml`-pinned localhost `zephyrus_test` configuration, but this isolated worktree + has no `vendor/autoload.php` and the host has no Composer executable. It failed before + Laravel bootstrap or any database connection and is not counted as passing evidence. +- This slice changes no Laravel route/controller/service/model/migration/configuration and + no iOS or Android application source. The executable Nightingale OpenAPI artifact still + has zero paths, and the native applications still have no network client or Android + internet permission. +- Candidate fixture success is not API success, clinical approval, identity approval, or + cross-platform runtime parity. Named ownership, route/compatibility ADR, identity and + source definitions, opaque-handle design, privacy/security/accessibility/patient/support + review, implementation tests, non-production integration, and release approval all + remain open. +- No production database, patient, principal, identity link, grant, session, source + encounter, feature flag, route, migration, deployment, or pilot state was read or changed. + +## 2026-07-26 — Route, compatibility, identity, and inpatient-source foundation + +### Source reconciliation + +- Traced Laravel route ownership through `RouteServiceProvider`, `routes/patient.php`, and + the bootstrap middleware/exception pipeline. The legacy patient API is explicitly mounted + under `/api/patient/v1`; staff/general mobile behavior remains under `/api/**`, including + `/api/mobile/v1`. +- Reconciled `Encounter`, the `prod.encounters` creation migration, + `PatientEncounterAccessService`, `PatientCommunicationEncounterGuard`, and + `PatientCommunicationLifecycleReconciliationService`. +- Identified that current operational code uses related but non-identical active/current + checks. The model scope checks status and deletion, messaging additionally requires a null + discharge timestamp and grant linkage, and lifecycle reconciliation treats contradictory + combinations as unresolved. +- Confirmed the encounter migration documents `active | discharged` but does not add a + database status check constraint. The operational table is therefore a plausible future + adapter input, not an authorization source or approved Nightingale vocabulary. +- Performed the audit from repository source only. No production database, patient, + principal, link, grant, session, encounter, configuration, or response was read. + +### Adopted architecture + +- Added the + [route, compatibility, identity, and inpatient-source ADR](../nightingale/ROUTE-COMPATIBILITY-IDENTITY-SOURCE-ADR-2026-07-26.md). +- Reserved `/api/nightingale/v1` and the held relative candidate path + `/inpatient-contexts`, with operation ID `listNightingaleInpatientContexts`. +- Rejected aliases, proxies, redirects, second-prefix route mounting, and native fallback to + the legacy patient or staff APIs. Internal reuse remains possible only behind + Nightingale-owned adapters and contracts. +- Updated the foundation and candidate artifacts to pin those design identifiers while + retaining zero OpenAPI paths, a `.invalid` server, no security scheme, no route + registration, no client generation/networking, and every activation state false. +- Added `config/nightingale.php` as a code-owned, non-environment-activatable foundation. + It cannot configure a provider/source adapter or turn on routes, identity, source queries, + disclosure, mutation, or production. + +### Default-deny backend primitives + +- Added a request-scoped `NightingaleIdentityBoundary` with `unavailable`, `denied`, and + `verified_self` states. The only implementation is unconfigured and always unavailable. + No legacy principal, token, ability, grant, credential, or identifier schema was adopted. +- Added a request-scoped `NightingaleInpatientContextSource` with `unavailable`, + `inconsistent`, `confirmed_closed`, and `confirmed_current` states. The only implementation + performs no query and always returns unavailable. +- Added a precondition gate that withholds for 11 of 12 identity/source combinations. The + single positive combination continues only to later governed evaluation; it never grants + access and never returns patient data. +- Added no service-container binding, middleware, controller, route file, route group, + model, migration, database call, network call, serializer, native client, or Android + internet permission. + +### Mechanical evidence + +- Added a dependency-free PHP verifier that cross-checks config, foundation, candidate, + route absence, unconfigured states, and all 12 precondition combinations. +- Added five negative mutations for route activation, identity activation, legacy-realm + reuse, production source-query permission, and namespace drift. +- Extended the foundation verifier to pin the namespace, no-alias strategy, and additional + route/source activation fields. +- Extended the candidate verifier to pin the relative path and operation ID while preserving + the zero-path/held state; added a candidate-path mutation. +- Added a PHPUnit truth-table test for the two ports and precondition gate. Local dependency- + free verification runs without Laravel or a database; the repository PHPUnit suite will + run the test after Composer installation in CI. +- Added PHP 8.2 setup and the backend verifier to the independent Nightingale CI job. +- Locally passed PHP syntax checks, the dependency-free backend verifier and its five + negative mutations, both Node contract verifiers and their negative mutations, the native + product-boundary scan, targeted Prettier checks, relative-link checks, Git whitespace + validation, and changed-file production credential/host scans. +- Regenerated the Nightingale Xcode project and passed five unit tests plus two UI journeys + on the iPhone 17 Pro Simulator with normal local simulator signing. A preceding + `CODE_SIGNING_ALLOWED=NO` diagnostic run correctly failed only the Keychain canary with + `errSecMissingEntitlement (-34018)`; it is not counted as evidence. The signed rerun passed + the canary, privacy cover, no-live-access, protected-state, and volatile-input tests. +- Built the Nightingale iOS Release simulator app successfully. +- On the Android API 35 `hb` emulator, passed all five instrumentation tests, the JVM unit + suite, the Nightingale product boundary, Android lint-vital, and Debug/Release assembly. + +### Checklist and holds + +- Checked the plan’s route/compatibility foundation item and the contract matrix’s ADR item. + Identity proofing/session/recovery, an authoritative source adapter, named owners, + independent approvals, and every operation/release gate remain unchecked. +- The earlier devlog entry accurately records the candidate’s prior null-path/no-reservation + state. This section supersedes that design state only; it does not turn the candidate into + an operation. +- No production patient was created. Production database access remains explicitly outside + this development stream, and no route, migration, deployment, or feature activation was + performed. + +## 2026-07-26 — Identity/session/recovery and current-inpatient held candidates + +### Source scrutiny and decisions + +- Reconciled the legacy patient auth service, patient-realm middleware, auth controller and + requests, principal/identity-link/challenge/session models, identity schema migration, + auth lifecycle tests, and session-management tests. Recorded exact SHA-256 evidence in the + [candidate decision](../nightingale/IDENTITY-SESSION-RECOVERY-AND-SOURCE-CANDIDATE-DECISION-2026-07-26.md). +- Confirmed the legacy reference selects local email/password credentials, Hummingbird token + abilities, persistent refresh families, session/device metadata, and a two-part enrollment + challenge. Those are evidence inputs and were not adopted as Nightingale requirements. +- Confirmed the schema contains patient/representative principal and relationship vocabulary + plus recovery/invitation challenge purposes, but the reviewed repository has no complete + runnable recovery or representative invitation/acceptance/revocation lifecycle. Schema + vocabulary was not treated as implementation or legal authority. +- Reconciled the current-inpatient evidence from `Encounter`, encounter-access, messaging + guard, and lifecycle reconciliation sources. Multiple related but non-identical current + definitions remain; no operational table or status value was promoted directly to + Nightingale authorization. +- Performed the work from source and synthetic fixtures only. No production database, + patient, principal, identity link, grant, session, encounter, response, or credential was + read, replayed, inserted, changed, or used for validation. + +### Held machine-readable candidates + +- Added a non-runnable identity/session/recovery candidate with null provider, credential, + refresh credential, enrollment channel, recovery channel, route, and operation. Every + identity, enrollment, session, recovery, representative, integration, and production + activation remains false. +- Restricted the initial positive identity state to self. Staff and legacy patient realms + are rejected, representatives remain held, access-credential persistence and durable + device identifiers remain prohibited, and `verified_self` explicitly does not authorize + patient access. +- Added 64 synthetic identity cases spanning activation, provider/evidence/assurance, + principal, session, identity-link, recovery, representative, audit, policy, and race + outcomes. Every case has a pinned result and audit mode. +- Added a non-runnable current-inpatient source candidate with null adapter, query contract, + database connection, route, operation, policy version, evaluation clock, and freshness + threshold. Every source/cohort/integration/production activation remains false. +- Defined unavailable, inconsistent, confirmed-closed, and confirmed-current source states. + Only confirmed-current may continue to later governed evaluation, and it still does not + authorize access. +- Added 42 synthetic source cases spanning positive/closed, activation, dependencies, + freshness, scope, linkage, lifecycle, completeness, concurrency, transitions, + cardinality, policy, and audit. Missing/stale evidence cannot become current; no record or + outage cannot become confirmed closed; contradictions cannot become a false empty result. + +### Mechanical evidence + +- Added `verify-nightingale-identity-source-candidates.mjs`. It cross-checks the empty + foundation contract, disabled PHP configuration, PHP enum vocabularies, held candidate + fields, all 106 exact case outcomes/audit modes, synthetic-only/no-production-replay + declarations, null freshness choices, and credential/source-identifier containment. +- Added nine negative mutations that must reject identity-provider selection, + representative activation, identity-as-authorization, a weakened cross-principal session + outcome, production replay, source-query activation, stale-as-current classification, an + OpenAPI runtime path, and a configured legacy source adapter. +- Added the verifier and its negative self-tests to the docs-sensitive Nightingale CI job. +- Updated the product plan, contract checklist, migration classification, protected-state + decision, Nightingale index, and this execution log immediately after the verified + candidate-design slice. +- Re-ran the empty-contract, encounter-candidate, identity/source-candidate, dependency-free + backend, and native product-boundary verifiers, including all negative self-tests. + JavaScript syntax, JSON parsing, targeted formatting, relative links, Git whitespace, and + changed-slice production-connection-token scans pass. +- Re-ran the normally signed iPhone 17 Pro Simulator suite: five unit tests and two UI + journeys passed with zero failures, including the real Keychain canary. The iOS Release + simulator build passed. +- Re-ran Android on the `hb` API 35 emulator: five instrumentation tests passed with zero + failures. A forced fresh JVM run passed five tests; the product boundary, lint-vital, and + Debug/Release assemblies passed. +- Two preliminary Android runner invocations failed before instrumentation because the + shell lacked an explicit SDK path and then reaped a detached emulator. Neither is counted + as product evidence; the same tasks passed after setting the Java/SDK paths and keeping + the API 35 emulator in a persistent session. + +### Holds + +- This completes candidate-state design and synthetic fixture coverage only. No provider, + proofing method, credential, session representation, enrollment/recovery channel, + representative authority, authoritative source, adapter/query, cohort, lifecycle mapping, + freshness threshold, or patient-facing language is approved. +- Identity, source, authorization, privacy/security, clinical operations, legal/HIM, + accessibility, patient-advisor, support, data-governance, integration, pilot, release, and + deployment approvals remain open. +- The Nightingale OpenAPI document still has zero paths, Laravel still has zero Nightingale + routes or bindings, both native apps still have zero network clients, and the correct + runtime user experience remains the no-live-access foundation shell. + +## 2026-07-26 — Cross-surface brand ownership and source-predecessor upgrade evidence + +### Repository and runtime audit + +- Added the + [cross-surface brand and upgrade audit](../evidence/nightingale/brand-identity-2026-07-26/CROSS-SURFACE-AUDIT.md). + It inventories Hummingbird and Nightingale application IDs, display names, notification, + APNs, WidgetKit, Live Activity, app-group, Android widget, shortcut, generated-asset, and + store-package surfaces. +- Confirmed Hummingbird owns the repository's only current widget surfaces: + `net.acumenus.hummingbird.HummingbirdWidgets`, + `group.net.acumenus.hummingbird`, the iOS WidgetKit/Live Activity bundle, and the + non-exported Android `HouseGlanceReceiver`. These sources contain no Nightingale name, + namespace, or extension-owned raster/vector artwork. +- Confirmed Hummingbird iOS uses system notification presentation under the staff + application identity and has no custom notification service/content extension or + attachment. Android registers four Hummingbird urgency channels but has no FCM service, + posting implementation, runtime notification permission, or small-icon selection; no + Android notification visual is claimed. +- Confirmed Nightingale remains notification-, push-, widget-, extension-, Live Activity-, + app-group-, shortcut-, receiver-, and network-free. This absence is the approved + foundation behavior. +- Confirmed no repository-owned App Store/Play Store listing directory or store screenshot + asset exists under the four native product roots. Existing launcher/splash captures remain + engineering evidence, not distribution material. + +### Mechanical controls + +- Added `verify-mobile-brand-surfaces.sh` to pin exact Hummingbird, Nightingale, and legacy + patient-reference identities; Hummingbird widget/push namespace ownership; Nightingale + negative surfaces; store-package absence; and cross-product name isolation. +- Extended `verify-mobile-brand-assets.sh` to reject identical in-app brand marks and every + corresponding Android launcher, round, adaptive-foreground, and monochrome asset at every + density. +- Wired both verifiers into the independent macOS mobile-brand CI job. Both scripts pass + Bash syntax and live verification. + +### Installed-upgrade emulator evidence + +- Built predecessor Hummingbird artifacts from detached `origin/main` source + `84b5f8305a694128423ae489fa4527e4b542927f` and current artifacts from + `06441141edd38ffd79803a5aed8b8906f6a32a20`. +- On iPhone 17 Pro Simulator, installed predecessor Hummingbird, wrote a synthetic + application-private canary, installed current Hummingbird over it, and read the exact + canary afterward. Both artifacts retained app bundle ID `net.acumenus.hummingbird` and + widget bundle ID `net.acumenus.hummingbird.HummingbirdWidgets`. The simulator reassigned + the raw container path while preserving the logical app data, so physical path equality + is correctly not treated as an application contract. +- On the Android API 35 `hb` emulator, repeated the clean predecessor/current replacement + with `adb install -r`. The exact private canary survived; package ID remained + `net.acumenus.hummingbird`; and both debug APKs had signer SHA-256 + `663f448870c7cf490608d8816a6d0d93cc5f441c0ff8190f53f02155f19df281`. +- The predecessor/current artifacts use equal development version/build values. The audit + therefore classifies this as source-predecessor engineering compatibility, not App + Store/Play Store acceptance, released-user migration, or distribution-signing proof. +- Updated Stream B and the product identity checklist immediately: repository-level + ownership and engineering upgrade checks are complete, while retained released-artifact, + external store, signing, version/build increment, installed-widget, protected-state, + rollback, and actual notification-rendering evidence remain unchecked. + +### Final regression + +- Re-ran both mobile-brand verifiers, all three Nightingale contract/candidate verifiers + with negative self-tests, the dependency-free backend verifier with negative self-tests, + and the native product-boundary verifier. Targeted Prettier, relative-link, Git + whitespace, and changed-slice production-connection-token checks pass. +- Regenerated the Nightingale Xcode project and passed the normally signed iPhone 17 Pro + Simulator suite: five unit tests and two UI journeys passed with zero failures. The + Nightingale Release simulator build passed. +- On the live Android API 35 `hb` emulator, five instrumentation tests passed with zero + failures. A forced fresh run passed five JVM tests; the Nightingale product boundary, + lint-vital, Debug assembly, and Release assembly passed. +- The current Hummingbird iOS Release simulator build, including embedded WidgetKit + validation, passed. The Hummingbird Android Release assembly and lint-vital task passed. +- Draft PR CI exposed that the clean Ubuntu and macOS runners do not preinstall `rg`. The + first Nightingale-boundary and mobile-brand jobs therefore failed before evaluating + product assertions. Replaced `rg` only inside the three affected standalone verification + scripts with portable recursive/fixed/extended `grep` equivalents, then re-ran Bash + syntax, both brand verifiers, the native boundary, all contract/backend verifiers and + negative self-tests, and Git whitespace checks locally. This is a CI portability fix, not + a product-boundary relaxation. + +### Safety and release holds + +- No production patient, database, credential, principal, encounter, route, feature flag, + migration, deployment, store record, signing key, APNs delivery, FCM delivery, or pilot + state was read or changed. +- Artwork rights, product-design/accessibility review, Apple/Google ownership, public + support, privacy disclosure, distribution signing, released-artifact migration, and + release authorization remain open. + +## 2026-07-26 — Identity input, recovery, first-read, and error source classification + +### Exact evidence inventory + +- Added the + [detailed source classification](../nightingale/IDENTITY-INPUT-FIRST-READ-ERROR-SOURCE-CLASSIFICATION-2026-07-26.md) + and a machine-readable source ledger covering 65 exact files at source commit + `b1078405de2dacd767ec69da11197f1e755d8277`. +- Classified 1 legacy contract, 33 backend/database sources, 8 backend tests, 11 iOS + sources/tests, and 12 Android sources/tests. Every row records its repository-relative + path, SHA-256, surface, one or more of four review domains, disposition, and + source-specific decision. +- The overlapping domains contain 44 identity-input sources, 37 enrollment/recovery + sources, 47 first-read projection sources, and 65 error/non-disclosure sources. +- Used source and synthetic repository evidence only. No production database, patient, + principal, identity link, challenge, grant, session, projection, credential, source + response, feature flag, migration, or deployment state was read or changed. + +### Material findings + +- Confirmed there is no complete patient account-recovery implementation. The identity + migration permits an `account_recovery` challenge purpose and native models recognize a + recovery auth-method label, but there is no recovery route, contract, proofing service, + UI, completion transaction, old-session invalidation policy, support path, or end-to-end + recovery test. +- Confirmed iOS silently selects `encounters.data.encounters.first` and Android silently + selects `firstOrNull()`. Backend ordering by `valid_from DESC` and grant UUID is not a + patient selection or clinically governed reconciliation rule. +- Confirmed both clients use raw grant scopes for request fan-out. iOS collapses projection + `404` to absence; Android collapses `403` and `404`; both collapse incompatible + patient-state vocabulary to absence. These states cannot safely stand for a genuine empty + care projection. +- Confirmed iOS can substitute newly generated random UUIDs for malformed schedule, pathway + stage, and care-team member identifiers. Nightingale must reject the affected projection + instead of inventing a server identity. +- Confirmed iOS can display `.server` and `.unauthorized` response messages directly, while + Android can display the decoded server message for HTTP 422. Nightingale must map stable + product-owned codes to reviewed localized client copy and never pass through arbitrary + server messages. +- Preserved candidate server principles including product/operation default-off gates, + exact realm/session ownership, one-way challenge secrets, row locking and atomic + consumption, session rotation/reuse handling, generic anti-oracle projection responses, + request-time authorization, allowlist-first patient projection content, durable + audit-before-disclosure, and no-store/no-index response handling. +- Rejected legacy email/password and seven-field invitation defaults, Hummingbird routes, + provider/models/tokens/storage/device identity, raw grant fields/scopes, first-record + selection, random identifier substitution, server-message passthrough, and nullable + projection-disposition conflation. + +### Mechanical enforcement + +- Added `scripts/ci/verify-nightingale-source-classification.mjs` and wired it into the + docs-sensitive Nightingale CI job. +- The verifier pins the exact 65-path inventory and every source SHA-256; validates strict + per-row fields, category counts, surfaces, dispositions, and substantive decisions; and + confirms the Nightingale configuration and zero-operation contract remain dormant. +- The ledger keeps fourteen permissions false, including implementation, runtime adoption, + route registration, legacy alias/provider/credential/device reuse, first-record + selection, server-message passthrough, projection-absence conflation, production data, + production queries, production replay, and patient/principal creation. +- Negative self-tests prove rejection of implementation/runtime activation, first-record + selection, server-message passthrough, absence conflation, production replay, a malformed + hash, source removal, duplicate/replaced inventory, and an `approved` disposition. +- Updated the product plan immediately after the bounded slice passed. The 65-file + classification item is complete; the broader all-source migration item remains open for + journey, communication, notification, preference, presentation, synthetic/debug, and + release sources. + +### Native and static regression evidence + +- Ran the new source-classification verifier with all negative mutations, the empty-contract + verifier, encounter-access candidate verifier, identity/source candidate verifier, + dependency-free backend verifier, native product-boundary verifier, and both cross-product + mobile-brand verifiers. All passed. +- Regenerated and compared the Nightingale Xcode project with `project.yml`; no generated + drift remained. On the normally signed iPhone 17 Pro Simulator, all five unit tests and + both UI tests passed with zero failures. The Release simulator build passed. +- Started the `hb` Android API 35 emulator and forced fresh execution without the Gradle + build cache. All five instrumentation tests and five JVM boundary tests passed with zero + failures. `verifyNightingaleProductBoundary`, `lintVitalRelease`, `assembleDebug`, and + `assembleRelease` passed in the same successful build. +- Shut down both emulators after evidence collection. +- One preliminary Xcode-project helper invocation supplied the repository root instead of + its required iOS-app root and correctly exited before generation. One preliminary Gradle + invocation stopped before task execution because macOS had no system-registered Java + runtime. Neither is counted as product evidence; the exact checks passed after supplying + `Nightingale/iosApp` and Android Studio's bundled JDK 21 respectively. + +### Holds + +- No identity provider, proofing method, enrollment or recovery workflow, representative + authority, session/credential model, authoritative source adapter, route, operation, + projection contract, error catalog, patient copy, or production access is approved. +- The Nightingale OpenAPI artifact still has zero paths, Laravel still registers no + Nightingale route, both apps still have no network client, and no patient-facing data + capability was added. +- Identity, source, privacy/security, legal/HIM, clinical safety, accessibility/language, + patient-advisor, support, operations, pilot, release, and deployment approvals remain + open. + +## 2026-07-26 — Communication and notification source classification + +### Exact evidence inventory + +- Added the + [communication and notification source classification](../nightingale/COMMUNICATION-AND-NOTIFICATION-SOURCE-CLASSIFICATION-2026-07-26.md) + and a machine-readable checksum ledger covering 130 unique files at source commit + `be8405a0f768bf239862b790b3eeae80b8aad2ad`. +- Classified 3 contract/reference sources, 65 backend sources, 5 database migrations, 9 + backend tests, 12 patient iOS sources/tests, 13 patient Android sources/tests, 8 staff iOS, + 7 staff Android, and 8 staff web sources/tests. +- The six overlapping review domains contain 77 patient-contract, 103 + patient-mutation/delivery, 89 staff-handoff/routing, 69 + notification-registration/delivery, 48 native-patient-experience, and 130 + error/offline/urgency sources. +- Recorded 55 evidence-only, 27 principle-only, 42 held, and 6 rejected dispositions through + 17 reusable decision records. Every source retains an individual SHA-256 digest. + +### Material findings + +- Confirmed the server message core has strong candidate safety properties: current + encounter/grant and thread revalidation, operation and client-message advisory locks, + payload digests, immutable encrypted bodies, append-only receipts and routing, content-free + staff outbox facts, optimistic concurrency, explicit responsibility pools, effective + eligible responders, fresh consumer heartbeats, and attributable staff actions. +- Confirmed patient notification delivery is absent. The backend registers, encrypts, + rebinds, and revokes provider tokens but has no APNs/FCM patient provider worker, approved + payload, delivery lifecycle, or provider receipt reconciliation. The iOS and Android + patient clients have no corresponding native registration/permission/service lifecycle. +- Confirmed patient clients have manual refresh only. Unlike the staff web/iOS/Android + surfaces, they have no foreground polling, and patient push is absent. +- Confirmed a human retry after an ambiguous transport outcome generates fresh idempotency + and client-message identities on both patient clients. The server can safely replay the + same identifiers, but a later user retry appears as a new mutation and may duplicate a + request already committed. +- Confirmed `server_accepted`/`sent` is written before the asynchronous `staff_inbox` + projection. Existing iOS and Android success wording therefore overstates what is proven + by saying the message was sent to the care team or responsible pool. +- Confirmed a decode-breaking state mismatch: escalation appends patient-visible delivery + state `escalated`, serialization returns that raw state, while the patient contract and + iOS delivery enum omit it. Android accepts raw strings but does not precisely map every + valid ownership/delivery state and checks non-contract ownership value + `team_acknowledged`. +- Confirmed topic and urgent-help content are static English configuration/code values + rather than locale-, checksum-, review-, effective-period-, and rollback-bound Nightingale + release content. iOS and Android do not have proven urgent/offline copy parity. +- Disproved a suspected staff-close decode problem: staff-specific operational reasons stay + on the staff work item while the patient thread is normalized to `question_answered`, an + accepted patient contract/iOS value. The negative finding is pinned to prevent future + overstatement. + +### Mechanical enforcement + +- Added + `scripts/ci/build-nightingale-communication-notification-classification.mjs` so the + explicit curated inventory and hashes can be reproduced after an intentional + reclassification. +- Added + `scripts/ci/verify-nightingale-communication-notification-classification.mjs` to pin the + exact 130 paths, inventory digest, individual hashes, six domain counts, 11 surface + counts, four disposition counts, 17 decisions, and ten material findings. +- Wired the verifier and its negative self-tests into the docs-sensitive Nightingale CI job. +- The ledger keeps 20 permissions false, including implementation/runtime/route/legacy + alias/copy adoption, provider/device/channel/payload activation, patient polling, offline + mutation queuing, retry identity regeneration, acceptance-as-delivery, production + data/query/replay, and patient/principal creation. +- Negative self-tests prove rejection of notification provider activation, patient polling, + offline queueing, retry identity regeneration, acceptance-as-delivery, production replay, + false resolution of the iOS escalation mismatch, false assertion of the staff-close + hypothesis, malformed hashes, source removal, duplicate inventory paths, and an + `approved` disposition. +- Updated the master plan, migration classification, contract/authorization checklist, + Nightingale documentation index, and this execution log immediately after the bounded + slice passed. The broader all-source classification remains open for journey, preference, + presentation, synthetic/debug, and release sources. + +### Native and static regression evidence + +- The new verifier with all negative mutations, the 65-file source verifier, empty-contract + verifier, encounter-access candidate verifier, identity/source candidate verifier, + dependency-free backend verifier, native product-boundary verifier, targeted Prettier, + and Git whitespace checks passed. +- Regenerated and compared the Nightingale Xcode project with `project.yml`; no generated + drift remained. On iPhone 17 Pro Simulator + `0A7FAE8C-8902-462D-BB4D-1E216D5BFDC1`, all five unit tests and two UI tests passed with + zero failures. The signed Release simulator build passed. +- Started the `hb` API 35 / Android 15 AVD and forced fresh execution with the Android Studio + JDK 21 and no Gradle build cache. Five instrumentation tests and five JVM boundary tests + passed with zero failures. `verifyNightingaleProductBoundary`, `lintVitalRelease`, + `assembleDebug`, and `assembleRelease` passed in the same 116-task build. +- Shut down both emulators after evidence collection. + +### Safety and release holds + +- This section classifies predecessor evidence; it does not implement Nightingale + communication or notification functionality. +- The Nightingale contract still has zero paths, Laravel still registers no Nightingale + route, both apps remain network-disabled, and no provider, payload, patient poller, + offline queue, staff handoff, patient message, or delivery channel was added. +- No production database, patient, principal, grant, session, provider console, runtime + credential, feature flag, migration, deployment, or release state was read or changed. +- Communication product/clinical operations, state vocabulary, patient language, + localization, accessibility, privacy/security, identity, source, provider, support, + incident/downtime, pilot, release, and deployment approvals remain open. + +## 2026-07-26 — Complete 255-source migration classification + +### Exact inventory closure + +- Defined the complete tracked Hummingbird Patient product universe in executable builder + and verifier logic: both patient-native roots; legacy patient configuration and route; + patient controllers, requests, models, policies, contracts, and services; patient tests; + patient-named migrations excluding the unrelated `patient_flow` subsystem; and + Hummingbird console commands whose filename contains `Patient`. +- The universe contains exactly 255 paths with inventory digest + `d6f680b73278786f8004826029e6a9413f921db4ce03df8873bde4c23c62d99c`. + The two prior ledgers cover 122 unique paths in that universe. +- Added the + [journey, preference, presentation, synthetic, and release classification](../nightingale/JOURNEY-PREFERENCE-PRESENTATION-RELEASE-SOURCE-CLASSIFICATION-2026-07-26.md) + and a machine-readable ledger for the exact remaining 133 paths at reviewed source commit + `e8f2b33bca79c4134f2476f41702430da72816d7`. Its path-list digest is + `dd74e3d050839815f731b02af1b2d3d4886e1837913f17e8bc87244c4ad172d2`. +- Classified the final sources as 41 reusable safety primitives, 20 held reusable product + behaviors, 28 test/fixture-only sources, and 44 rejected legacy behaviors. Every source + has one exact class, disposition, surface, decision, domain set, and byte SHA-256. +- The three ledgers now cover 255 of 255 product sources. This closes source classification, + not migration, functional parity, implementation, approval, or release. + +### Patient-journey findings + +- Confirmed both legacy native apps expose Messages as a fourth top-level destination, + conflicting with the Nightingale charter’s Today/My Path/Care Team primary structure and + contextual communication entry points. +- Confirmed iOS combines multiple projection timestamps, stale flags, provenance, and + uncertainty into one aggregate snapshot context. This cannot prove field-level context + for the particular screen/card a patient is reading. +- Confirmed Android renders pathway, pathway events, discharge readiness, and rounds + summary in one My Path view but selects only the first available projection context for + the header. The remaining displayed subprojections can have different provenance, + freshness, and uncertainty. +- Carried forward the already proven unsafe first-record encounter selection on both + platforms. Nightingale’s zero/one candidate and fail-closed multiple-context behavior + remain required. +- Preserved released-only empty states, patient-safe uncertainty, revision notices, + urgent-help separation, care-team role framing, and nonclinical authored-input + separation as held product/safety requirements. +- Confirmed the independent clinical-review/catalog-release-manager service is a strong + pathway-specific principle but does not establish equivalent two-person release for + Today, care team, pathway events, discharge readiness, rounds, education, communication, + or notification content. + +### Preference and accessibility findings + +- Reconciled seven server preference fields against five fields exposed by each native + editor. Locale and timezone are not editable natively; server-only `sms` and `none` + delivery values are omitted; native editors expose only push/email and default a missing + value to push despite the previously proven absence of patient push delivery. +- Confirmed both platforms preserve a stronger system text-size setting by taking the + maximum of the system and account-selected scale. +- Confirmed iOS uses the combined system/account reduced-motion choice for privacy-cover + and scenic-background transitions. +- Confirmed Android decodes, persists, propagates, and announces reduced motion but has no + main-source branch that changes rendering motion. The setting is semantically inert and + is not approved for Nightingale until behavior and test coverage exist. +- Required Nightingale to separate device accessibility, account presentation, locale, + communication consent, delivery channel, and notification-preview ownership instead of + copying one mixed preferences object. + +### Synthetic, provisioning, and release findings + +- Confirmed iOS synthetic activation/content are compiler-gated to Debug and Android uses + debug source-set fixtures plus inert Release stubs. The compile-exclusion property is a + reusable principle; all Hummingbird hooks, IDs, extras, payloads, and copy are test-only. +- Confirmed `SyntheticPatientProjectionProvisioner` refuses non-testing environments and + remains test-only. +- Rejected the command-accessible reference provisioners as Nightingale or production + tooling. Despite default-off, dry-run, synthetic-name, ownership, locking, pending-state, + redaction, and draft-only safeguards, their `--commit` paths can create or bind an + operational encounter, principal, identity link, grant, enrollment challenges/secrets, + policy, cursors, and projections in a deployed database. +- Rejected all legacy Hummingbird Patient project/package/bundle/version/activation and + icon/scenic/theme resources. Nightingale retains its independent app roots, IDs, supplied + nightingale artwork, and product boundary. +- Recorded the absence of repository proof for distribution signing, external store + records, retained released-artifact upgrades, monotonic version history, notification + identities, store privacy/support metadata, or pilot release. + +### Mechanical enforcement and checklist update + +- Added + `scripts/ci/build-nightingale-journey-preference-release-classification.mjs` to reproduce + the exact 133-source ledger only from the reviewed product universe. +- Added + `scripts/ci/verify-nightingale-journey-preference-release-classification.mjs` to pin the + 255/122/133 closure, both inventory digests, every file hash, 18 source surfaces, four + required classes/dispositions, 19 decisions, eight domain counts, and 23 material + findings. +- Ten negative mutations prove rejection of runtime adoption, production queries, source + omission, checksum drift, classification weakening, false Android reduced-motion + coverage, blanket migration approval, universe-count drift, inadequate decision + rationale, and synthetic release activation. +- Wired the new verifier into the docs-sensitive Nightingale CI job. +- Updated the master plan immediately after the verifier passed: the all-source + classification item is now complete, while every port, journey, accessibility, + human-review, pilot, release, and deployment item remains open. +- Updated the migration record, contract/authorization matrix, Nightingale documentation + index, and this execution log to reflect complete source classification without + overstating implementation. + +### Native and static regression evidence + +- Re-ran the empty-contract, encounter-access, identity/source, 65-source, + communication/notification, final 133-source, backend default-deny, native product + boundary, mobile brand-asset, and mobile brand-surface verifiers. Every positive and + negative assertion passed. +- Ran target-file Prettier, JavaScript syntax, generated-ledger equality, relative-link, + sensitive-literal, and Git whitespace checks. All passed. +- Regenerated the Nightingale Xcode project from `project.yml` and confirmed no generated + project drift. On iPhone 17 Pro Simulator + `0A7FAE8C-8902-462D-BB4D-1E216D5BFDC1`, all five unit tests and two UI tests passed with + zero failures. The normally signed Release simulator build passed. The simulator was + shut down after the run. +- Cold-booted the `hb` Android 15/API 35 AVD with wiped data and no snapshot. Using Android + Studio JDK 21 and `--no-build-cache`, all five instrumentation tests and five JVM tests + passed; `verifyNightingaleProductBoundary`, `lintVitalRelease`, `assembleDebug`, and + `assembleRelease` passed. Gradle reported 117 actionable tasks: 116 executed and one + up-to-date. The emulator was shut down after the run. +- A preliminary Android attempt reached `connectedDebugAndroidTest` after the JVM and + boundary tasks but correctly failed with `No connected devices!` because the headless + emulator process inherited and exited with its boot shell. A second detached boot was + stopped before Gradle when the same process-lifetime issue was confirmed. Neither attempt + is counted as device evidence. The final run held the emulator in a persistent foreground + session and reran the complete clean task set successfully. + +### Safety and release holds + +- This work is repository-source classification only. It adds no Nightingale route, + operation, identity provider, source adapter, client, network permission, projection, + preference persistence, message, notification, synthetic runtime, migration, or release. +- No production database, patient, principal, identity link, grant, session, encounter, + preference, message, goal, pathway draft/review/release, feature flag, migration, + deployment, or pilot state was read or changed. +- Named product, patient-advisor, clinical/content, nursing, medical-staff, pharmacy, + privacy/security, accessibility, language/interpreter, legal/HIM, identity, source, + support, operations, release, and deployment approvals remain open. + +## 2026-07-26 — Held Today projection candidate + +### Contract boundary + +- Added the + [Nightingale Today projection candidate decision](../nightingale/TODAY-PROJECTION-CANDIDATE-DECISION-2026-07-26.md) + and synthetic candidate artifacts under + `docs/nightingale/api-contract/candidates/today/v0/`. +- Reserved only the held intent + `GET /inpatient-contexts/{encounter_handle}/today` with operation ID + `getNightingaleTodayProjection`. The executable foundation still has zero paths, no + Laravel route is registered, and no backend or native runtime uses the candidate. +- The request accepts only the separately held Nightingale opaque inpatient-context handle. + It accepts no body, query, source identifier, legacy identifier, patient identifier, + principal identifier, grant identifier, or client-supplied authorization scope. +- Every candidate activation field remains false: product, operation, identity, inpatient + source, projection source, clinical content release, localization, disclosure, native + clients, non-production integration, and production. + +### Field and section decisions + +- Replaced the legacy composite document-level context with a complete governance context + beside every patient-visible value: release, freshness, uncertainty, language, + correction, and offline behavior. +- Defined exactly eight sections: headline, summary, schedule, next steps, care location, + discharge outlook, questions, and notices. Headline and summary are mandatory released + sections. +- Defined explicit `released`, `released-empty`, and `not-available` states for optional + sections. Released-empty carries a mandatory notice that zero released items does not + mean no care is planned. Not-available contains no patient content. +- Added Nightingale-only opaque content-revision and schedule-item handle formats. Legacy + encounter, grant, projection, principal, staff, patient, FHIR, EHR, and source identifiers + remain prohibited. +- Restricted freshness to current or explicitly approved stale. Approved stale requires a + field-level patient notice; unknown mandatory freshness fails closed. +- Required a field locale, approved source-language or translation release, and approved + plain-language review. Silent locale fallback and unapproved machine translation remain + prohibited. +- Required corrected values to carry a patient notice. A root correction notice cannot + reveal the withdrawn value, actor, reason, target/replacement identifier, or staff review + record. +- Kept every candidate field online-only with durable client storage prohibited. This does + not approve background refresh, push, notifications, offline storage, or cache + invalidation. + +### Synthetic coverage and mechanical enforcement + +- Added 68 synthetic cases across 18 bounded response templates: + - 12 governed success variants; + - 20 product, identity, authorization, non-disclosure, lifecycle, and handle-integrity + outcomes; + - 24 source, release, field-governance, language, freshness, content-safety, and + vocabulary outcomes; and + - 12 structure, audit, serialization, and throttling outcomes. +- Added one exact synthetic request template with only the held method, namespace, path, + valid opaque handle, empty query, null body, and authentication-context requirement. +- Added 14 direct source-byte checksums in addition to the already-pinned 255-source product + universe digest. The direct evidence spans the legacy patient contract, controller, + disclosure service, content guard, policy, model, migration, feature test, and both + native Today decode/presentation paths. +- Added an independent verifier that checks the zero-path foundation, every disabled + permission, exact root/evidence/request/response/field schemas, no-store headers, empty + response links, error-code mapping, all fixture outcomes and audit modes, field context + completeness, section-state contradictions, handle formats/uniqueness, deterministic + timestamp order, source hashes, and prohibited environment/credential/legacy identifier + literals. +- Twenty-four adversarial self-tests prove rejection of path drift, OpenAPI inclusion, + production activation, aggregate freshness, missing field context, stale content without + a notice, locale drift, released-empty content, production replay, foundation path + activation, checksum drift, fixture removal, durable client caching, and semantic drift + in every governed success variant. +- Wired the verifier into the Nightingale contract-foundation CI job. +- Updated the master checklist, route ADR, migration record, authorization matrix, and + Nightingale documentation index immediately after the candidate verifier passed. + +### Verification and safety status + +- Before this slice, exact commit + `08837f8295e929cb860053a92b55772501eab61f` completed CI run + `30216291263` with all 18 jobs successful, including patient iOS/Android, staff + iOS/Android, backend quality and shards, frontend, browser, DAST, Arena, security, brand, + and Nightingale contract gates. +- The Today builder reproduces the candidate artifacts; JavaScript syntax, all seven + Nightingale governance/classification verifiers and their negative tests, the + dependency-free backend foundation, native product boundary, mobile brand asset/surface, + target-file Prettier, relative-link, sensitive-literal, and Git whitespace checks pass. +- Regenerated the Nightingale Xcode project and confirmed no drift. On iPhone 16e Simulator + `3F568F29-BE58-49AD-8151-6C2303B4C4E3` running iOS 26.3.1, all five unit tests and two UI + tests passed with zero failures. The normally signed Release simulator build passed, and + the simulator was shut down. +- Cold-booted the wiped `hb` Android 15/API 35 AVD with no snapshot and ran Gradle from + `clean` with Android Studio JDK 21 and `--no-build-cache`. Five JVM tests and five + instrumentation tests passed; `verifyNightingaleProductBoundary`, `lintVitalRelease`, + `assembleDebug`, and `assembleRelease` passed. Gradle reported 117 actionable tasks: 116 + executed and one up-to-date. The emulator was shut down. +- Commit/push and exact-SHA CI evidence for this Today slice remain required before the + slice is fully ratified. +- No production database, patient, principal, identity link, grant, session, encounter, + projection, content release, source, feature flag, migration, deployment, or pilot state + was read or changed. +- Named identity/source, field-level clinical/content/language, privacy/security, + accessibility, patient-advisor, legal/HIM, support, operations, release, and deployment + approvals remain open. No route, adapter, query, generated client, or native journey may + be implemented from this candidate until those gates are independently satisfied. + +## 2026-07-26 — Device-local display-comfort foundation + +### Decision and patient boundary + +- Added the + [presentation-preferences foundation decision](../nightingale/PRESENTATION-PREFERENCES-FOUNDATION-DECISION-2026-07-26.md). +- Implemented exactly two Nightingale-owned device choices on iOS and Android: reduce + motion and hide decorative imagery. +- Kept both choices separate from care-account, locale, clinical, communication, consent, + delivery, notification, identity, and encounter state. The patient-facing card explicitly + says the settings never change care information. +- Chose stronger-setting precedence: either a system reduced-motion request or the local + Nightingale request removes governed decorative motion; either the local imagery choice + or a stronger system contrast/transparency condition withholds decorative art. +- Decided that Nightingale will not roam these settings through an account, API, or cloud + key-value store. Android backup/device-transfer rules exclude shared preferences. iOS + operating-system backup/restore behavior remains unratified and is not represented as an + app synchronization feature. + +### Native implementation + +- Added a pure iOS presentation policy and `UserDefaults`-backed Nightingale namespace with + full product-specific keys. The UI-test reset hook is compiled only in Debug. +- Added a pure Android presentation policy and private Nightingale `SharedPreferences` + namespace. Writes commit synchronously, and the UI snapshot changes only after a + successful commit. +- iOS now consumes system Reduce Motion, Reduce Transparency, Increased Contrast, + accessibility text size, and color scheme. Android consumes the zero animator-duration + condition, Android 14+ system contrast, and current font scale. +- Both apps hide the foreground and scenic bird artwork when requested, retain every + essential heading/copy/control, and attenuate imagery at accessibility text size. +- iOS removes privacy-cover and local presentation transitions under reduced motion. + Android replaces the scenic alpha tween with `snap()` and a zero-millisecond policy. +- Added equivalent Nightingale-only labels, identifiers/test tags, checked states, and + effect explanations on both platforms. + +### Mechanical enforcement + +- Extended `verify-nightingale-product-boundary.sh` to require the two policy sources and + exact preference namespaces, reject account/cloud-sync APIs, require the iOS reset hook + inside a Debug conditional, and reject production test-clearing/inspection APIs. +- Unit truth tables cover standard, local reduced/hidden, stronger system, and + accessibility-text behavior. +- Persistence tests prove the exact Nightingale key set and reject Hummingbird, patient, + account, and token namespace leakage. +- Device UI tests enable both controls, verify patient-facing effect copy, relaunch or + recreate the application, and verify the persisted checked states. + +### Native evidence + +- Regenerated the iOS Xcode project. On iPhone 16e Simulator + `3F568F29-BE58-49AD-8151-6C2303B4C4E3`, iOS 26.3.1, all 10 tests passed with zero + failures or skips. The normally signed Release simulator build succeeded. +- Visually inspected the clean installed iOS Release application. The supplied + Nightingale artwork remains calming and subordinate to text; the privacy boundary is + legible; the two controls and explanatory copy are reachable. +- The iOS Release executable scan excluded the Debug reset hook, Hummingbird name, + application network URLs, and cloud-key-value API. It contained only the expected Apple + property-list DTD URL among URL-shaped strings. +- The exact iOS Debug and Release simulator executable SHA-256 values are + `5f203f74c9b98f7700e00d52c05e0b676b8c458f07c385825cc59695709b3ca9` and + `663b1f9906e718dbd235512b1bc90cc9f9e47738a718bb71ac8cb686c6db0e81`. +- Cold-booted the `hb` Android 15/API 35 AVD without a snapshot. Six JVM and six + instrumentation tests passed. `verifyNightingaleProductBoundary`, `lintVitalRelease`, + `assembleDebug`, and `assembleRelease` passed; Gradle reported 117 actionable tasks, + with 116 executed and one up-to-date. +- A manual UI Automator check found the Nightingale heading, privacy copy, Display comfort + heading, two checkable controls, and their explanations. Both controls stayed checked + with enabled-state copy after a cold relaunch. +- Android `FLAG_SECURE` intentionally made external emulator screenshots black. The black + capture is not treated as visual-layout proof; Compose instrumentation and the live + accessibility hierarchy establish rendered semantics and state. Independent human + device review remains open. +- The Android unsigned Release APK declared no `INTERNET` permission. Its DEX excluded + legacy Hummingbird, test preference APIs, Zephyrus endpoint, API-path, and WebSocket + tokens. Generic AndroidX/Compose diagnostic/schema URLs were classified as library + strings rather than application endpoints. +- The exact Android Debug and unsigned Release APK SHA-256 values are + `ac851797682524dde8d739d9b6f4aa4eaaa0b77cc4e322b48e1ec21ec7149e1c` and + `9c23e30f1c7211f969ecb2cf68698591f74de409a3d689cf116c68e64fded3e1`. + +### Reproducibility and failed preflights + +- Initial Android preflights failed before product verification because the shell did not + select a Java runtime or Android SDK. Selecting the Android Studio JDK and local SDK then + exposed an unavailable high-text-contrast API and a missing Compose delegated-state + import. +- Replaced the unavailable API with the Android 14+ `UiModeManager.contrast` seam and added + the required import. The complete clean task set then passed. None of the preliminary + failures is counted as evidence. +- A broad Android Release URL scan found only platform/library diagnostics and schema + strings. The final boundary assertion uses forbidden application endpoint tokens plus + the independent no-`INTERNET` manifest check, and does not misrepresent bundled library + text as application networking. + +### Checklist and residual status + +- Updated the master plan immediately: the bounded display-comfort subset is complete, and + the broader primitive-port item remains open for patient API and vocabulary work. +- Closed the checklist decisions for device/account separation, intentional non-roaming, + effective reduced motion, and exact local artifact binding. +- Left the compound largest-text, reflow, focus, contrast, target-size, screen-reader, + landscape, language-expansion, and images-disabled item open because only the bounded + imagery subset is proven. +- Full WCAG 2.2 AA conformance, physical-device visual review, iOS backup/restore behavior, + distribution signing/store evidence, and named patient-advisor/accessibility/privacy/ + clinical/language/support/release approvals remain open. +- No production database, patient, principal, identity link, grant, session, encounter, + account preference, clinical projection, message, route, source, migration, deployment, + or pilot state was read or changed. + +## 2026-07-26 — Foundation accessibility and layout matrix + +### Audit finding and bounded correction + +- Added the + [foundation accessibility and layout matrix](../nightingale/FOUNDATION-ACCESSIBILITY-LAYOUT-MATRIX-2026-07-26.md). +- Audited the installed iOS foundation at dark appearance, Increased Contrast, and + `accessibility-extra-extra-extra-large`. The fixed forest accent measured 2.946:1 against + black and visibly failed the bounded normal-text contrast gate. +- Replaced the fixed iOS accent with a dynamic light/dark value. Unit-calculated contrast is + now 7.129:1 against the light system background and 11.324:1 against the dark system + background. +- Added an explicit Android Material 3 dark scheme selected from + `isSystemInDarkTheme()`. Eight Android text/container pairs across the light and dark + schemes now have unit-enforced ratios ranging from 6.031:1 to 16.342:1. +- Kept color as non-essential emphasis. Every current patient state still has explicit text, + and decorative imagery remains outside accessibility meaning. + +### Layout, semantics, and target behavior + +- Added stable identifiers/test tags for the product, privacy, and Display comfort headings + plus both current controls. +- Removed an unnecessary iOS contained accessibility subtree after XCUITest proved it placed + Display comfort before the visually earlier privacy card. The final accessibility + sequence is product heading, privacy heading, Display comfort heading, reduce motion, and + hide imagery. +- Added 44-point minimum iOS toggle rows and rectangular hit shapes. +- Replaced each Android label-plus-thumb interaction with one full-width, 48 dp minimum + `Role.Switch` row. The visual switch has no separate change handler, so the whole labeled + row is one state-changing target. +- Top-aligned and width-bounded the Android scroll column. This removes dependence on center + arrangement when enlarged content exceeds the viewport. +- Added an iOS accessibility-XXXL landscape journey that proves semantic order, + accessibility-aware auto-scroll/tap behavior, target height, and state changes. Its + `DynamicTypeSize.accessibility5` adapter is compile-time Debug-only, and the test + requires a 60-point product-heading height so a no-op adapter cannot pass. +- Added an Android font-scale-2.0 landscape journey that traverses the actual unmerged + semantics tree, proves ordering, converts 48 dp to device pixels, checks full-row heights, + changes both states, and restores font scale/orientation in `finally`. + +### Native and artifact evidence + +- On iPhone 16e Simulator `3F568F29-BE58-49AD-8151-6C2303B4C4E3`, iOS 26.3.1, the + final normally signed suite passed eight unit and four UI tests with zero failures or + skips from restored light appearance, normal contrast, and system-large text. The + largest-text journey itself applied the Debug-only accessibility5 override, produced a + seven-page landscape hierarchy, and passed. The normally signed Release simulator build + passed. +- The earlier Release visual inspection remains appearance and reflow evidence; it is not + used as landscape-contract evidence. For that inspection, the simulator was explicitly + set to dark appearance, + `DarkenSystemColors=1`, and accessibility XXXL. Text reflowed and scrolled, the corrected + accent remained legible, card boundaries were visible, and the stronger contrast policy + withheld decorative imagery. The local screenshot SHA-256 is + `f3ca5fb5eb5281424d08858e56fdd381eb06ed5fa47d607dfc1bfc480b08f153`. +- On the `hb` Android 15/API 35 AVD, seven JVM and seven instrumentation tests passed. + A clean, no-build-cache run also passed the Nightingale product boundary, `lintDebug`, + `lintVitalRelease`, Debug assembly, and unsigned Release assembly. Gradle reported 125 + actionable tasks: 124 executed and one up-to-date. +- The final Android dark/font-scale-2.0 UI Automator hierarchy exposed the two complete + checkable rows at 222 pixels each on the 420 dpi portrait surface, approximately 84.6 dp. + Its SHA-256 is + `6392d3ef5ebfbcd4ef85b568e94c067b90fb77725ef4197e85f7a82518d13fcf`. +- Android `FLAG_SECURE` still yielded a black application capture. The capture hash + `5d5c731e9047f2c26407fab4bfa66b40ae59788da3438089db5f479732b53ebc` + proves the capture boundary, not visual layout. Independent dark-theme physical/secure + device review remains open. +- Exact local artifacts: + - iOS Debug executable: + `92c57ad6fbbe680bdc77d8252c6a144d0b4b90f4a225acadc86159891b34fd1e`; + - iOS Release executable: + `182ef77a6a020c4a26212482f09822901f94e3587433bbe490e5cb55be2c4827`; + - iOS Release application manifest: + `cc49573008857a7a658978b871553c922bf928577a80a7cece3750e804f6ef0c`; + - Android Debug APK: + `4d866ec381399caabd1287fd204e0dc01d3794267aecd5900a69d87d0dd91164`; + - Android unsigned Release APK: + `bd3d2994c84fa7de97d2770c257b70b8eb48f0fc59a081f486ecf6ebe03dd4e3`. + +### Mechanical enforcement and retained failures + +- Extended `verify-nightingale-product-boundary.sh` to require the appearance-aware iOS + accent, 44-point iOS row target, Android dark scheme, system dark selection, 48 dp + Android row target, both contrast tests, both largest-text landscape journeys, and the + iOS accessibility-size adapter inside a Debug-only branch. +- Retained the failed fixed-color ratio and initial semantic-order assertion as findings. + Also retained, but did not count as evidence, an unsigned iOS Keychain status `-34018`, + an invalid receiver-scoped Compose import, clipped-coordinate order logic, and an + unavailable Compose semantics accessor. The first iOS geometry proof also failed at + `40.67` points because the UIKit launch argument did not affect the SwiftUI hierarchy on + iOS 26.3.1; the accepted Debug-only adapter produced the intended seven-page hierarchy. +- The master checklist now closes only the bounded current-shell matrix. Full WCAG 2.2 AA, + VoiceOver/TalkBack human traversal, focus recovery, language expansion/RTL, + physical-device review, every future journey, and all named reviews remain open. +- Commit/push and exact-SHA CI evidence for this slice remain required before publication is + fully ratified. +- No production database, patient, principal, identity link, grant, session, encounter, + projection, preference, message, route, source, migration, deployment, or pilot state was + read or changed. + +## Foundation landscape release-contract correction + +- A post-publication threat-surface inventory found that the iOS XCUITest rotated the + simulator, but the tracked and built Release `Info.plist` declared portrait only. + Therefore the earlier journey did not, by itself, prove ordinary distributed landscape + support. +- Added `UIInterfaceOrientationLandscapeLeft` and + `UIInterfaceOrientationLandscapeRight` to both `nightingale/iosApp/project.yml` and the + tracked Nightingale `Info.plist`. +- Strengthened the accessibility5 journey to require `XCUIApplication.frame.width` to + exceed its height before evaluating the seven-page landscape hierarchy, semantic order, + accessibility-aware auto-scroll, 44-point targets, and state changes. +- The first truly landscape run then exposed an invalid-activation-point failure when the + old helper queried `isHittable` for a still-offscreen switch. A center-in-viewport + remediation still admitted a switch whose frame began at y = -4 points; a + full-frame-in-viewport remediation then exposed XCTest's rotated-coordinate mismatch + between the 844 by 390 application frame and portrait-space auto-scroll hit coordinates. + Neither failed run is counted as evidence. The accepted journey waits for each element, + checks its 44-point minimum frame, lets XCTest auto-scroll during the accessibility + action, and requires the state-changing tap to succeed. Portrait restoration now lives + in XCTest teardown so it also runs after a framework-level interaction failure. +- Extended the native boundary verifier to fail if either supported landscape orientation + disappears from the project specification or application manifest. +- The final current-source iOS run passed all eight unit and four UI tests with no failure + or skip. The normally signed Release build passed; its built manifest contains portrait, + landscape-left, and landscape-right, and its executable excludes both Debug-only test + keys. Current artifact hashes are recorded in the accessibility matrix and above. +- Published the correction as commit + `80c29e9ff5a745e50e7efdca4c2ac9c3a3df8091`. Exact-SHA + [CI run 30223467440](https://github.com/sudoshi/Zephyrus/actions/runs/30223467440) + passed all 18 jobs with no failure or rerun. This supersedes the portrait-era + landscape-evidence claim while retaining its failed discovery history. + +## Foundation threat and clinical-hazard model + +### Method and scope + +- Added the draft + [foundation threat and clinical-hazard model](../nightingale/FOUNDATION-THREAT-AND-HAZARD-MODEL-2026-07-26.md). + It uses NIST CSF 2.0 as an outcome taxonomy, OWASP MASVS as a mobile-control checklist, + HHS risk-analysis/mobile-privacy guidance for confidentiality/integrity/availability and + personal-device context, NHS DCB0129/DCB0160 lifecycle concepts as a clinical-risk + reference, STRIDE, and repository-specific misuse cases. +- The model explicitly makes no NIST, MASVS, HIPAA, DCB0129/DCB0160, medical-device, + privacy-law, or other compliance claim. It records no named approval and accepts no + residual risk. +- Bound the assessment to executable baseline + `80c29e9ff5a745e50e7efdca4c2ac9c3a3df8091`: an offline native shell, zero OpenAPI paths, + zero registered Nightingale routes, no network client/permission, no identity/provider, + no source adapter/query, no patient disclosure/mutation, and no production activation. + Future conceptual components are threat boundaries, not implementation approval. + +### Registers and gates + +- Versioned 18 protected assets, 14 trust boundaries, 25 implemented-control claims with + explicit limits, 22 security/privacy threats, 22 clinical hazards, 18 abuse/misuse cases, + 20 activation gates, 18 verification tracks, eight incident classes, and 17 open risks. +- Used a dual view that avoids deceptively low “current risk” scores: every register entry + records inherent risk if activated without controls, while current exposure is separately + classified as blocked, present-foundation, future-design, or external. +- Marked wrong-principal/IDOR disclosure and urgent-message false assurance as critical + activation risks. Wrong encounter, stale/corrected/retracted content, representative + overreach, inaccessible or misunderstood content, lost/shared-device exposure, + routing/delivery ambiguity, support social engineering, source contradiction, and + absent rollback remain high activation risks. +- Recorded that “blocked by non-activation” does not mean safe or accepted: removing a + route/network/provider/source/disclosure barrier without the corresponding gate is a + release-blocking defect. +- Added explicit detection signals, containment classes, recovery proof, change triggers, + named-owner requirements, platform asymmetries, and an ordered ten-step implementation + sequence. The current foundation has no remote kill switch because it has no live + capability; a tested kill switch remains mandatory before activation. + +### Mechanical enforcement and current holds + +- Added `scripts/ci/verify-nightingale-threat-hazard-model.mjs` and wired its self-test mode + into the Nightingale CI job. It requires the complete identifier sets and draft/ + non-authorization statements, pins all five authoritative method links, rejects implied + approval checkboxes and prohibited compliance claims, and cross-checks the actual + backend/contract/native default-off boundaries. +- Eight negative self-tests prove rejection of a removed critical messaging hazard, removed + draft status, missing NIST input, implied approval checkbox, backend production + activation, inserted API operation, Android network permission, and iOS live-access + activation. +- The broader Stream E item remains open: independent clinical-safety/privacy/security + ratification, red-team, incident tabletop, penetration testing, signed-artifact mobile + assessment, dependency response, human accessibility/language/patient review, and every + named approval are still required. +- No production database, patient, principal, encounter, identity link, source, projection, + message, notification, credential, integration, migration, deployment, pilot, or release + state was read or changed. +- Published the model as commit `b772b0e3b45daf75fa60aa83721e0eb4ba163e3c`. + Exact-SHA + [CI run 30224139839](https://github.com/sudoshi/Zephyrus/actions/runs/30224139839) + passed all 18 jobs with no failure or rerun. + +## Foundation dependency inventory + +### Gap and bounded decision + +- Reconciled the Stream A exit evidence against the actual repository and found that the + required generated software-bill-of-materials/dependency-inventory evidence did not + exist. The identity checklist’s combined build/artifact/SBOM/signing/release-manifest row + also remained open. +- Chose a precise **governed foundation dependency inventory** instead of labeling a custom + file as a standards-conformant SBOM. The record explicitly excludes CycloneDX/SPDX + conformance, license conclusions, vulnerability/exploitability findings, artifact + checksums, registry/source provenance, build-plugin transitive graphs, signed artifacts, + and distribution approval. +- Kept the combined identity-checklist row open. Only the repository-local inventory + identity and current bounded inventory evidence are closed; build artifact names, + standards-form SBOM naming, signing, and release manifests remain unresolved. + +### Deterministic Android graph + +- Added the Gradle reporting task + `:app:writeNightingaleReleaseDependencyResolution`. It queries the structured + `ResolutionResult` for `releaseRuntimeClasspath`, records direct declarations, unique + external module components, and unique resolved dependency relationships, then + deterministically tuple-sorts the output. +- Added the cross-platform generator + `scripts/ci/generate-nightingale-foundation-dependency-inventory.mjs`. It invokes the + Gradle task with JDK 17, parses the structured report, derives declared build + requirements, inspects the iOS source boundary, computes exact source hashes, and writes + the canonical record without a timestamp. +- The current graph contains seven direct Android Release runtime declarations, 83 unique + resolved external components, and 457 unique dependency edges. The larger transitive + count is not represented as 83 first-order product choices. +- An initial local dependency diagnostic failed closed under the shell’s Java 8 runtime + because the build requires at least JVM 11. It is not evidence. Generation was rerun with + Android Studio’s JDK 17 and succeeded. + +### iOS boundary + +- Inspected the XcodeGen application-target source and the bounded iOS root. There is no + XcodeGen `packages:` block or target `package:` dependency and no `Package.swift`, + `Package.resolved`, `Podfile`, `Podfile.lock`, `Cartfile`, or `Cartfile.resolved`. +- Recorded zero iOS third-party runtime packages and the four Apple modules imported by the + application target: `Combine`, `Foundation`, `Security`, and `SwiftUI`. +- Explicitly limited the finding: it does not inventory Apple SDK/OS contents, Xcode or + compiler components, or a signed archive’s embedded binaries. + +### Source identity and enforcement + +- Added the canonical generated record at + `docs/nightingale/supply-chain/foundation-dependency-inventory.v0.json` using schema + `net.acumenus.nightingale.foundation-dependency-inventory`, version 1. +- Bound it by SHA-256 to the Android app/root/settings Gradle sources, Gradle wrapper + properties, iOS XcodeGen source, and generator. Any change to a dependency, repository, + plugin, wrapper, SDK declaration, project source, or generator requires regeneration and + review. +- Added + `scripts/ci/verify-nightingale-foundation-dependency-inventory.mjs` to the docs-sensitive + Nightingale CI job. It verifies product/release identity, scope limitations, source + hashes, exact direct declarations, selected component versions, graph uniqueness/order/ + referential integrity, reconciled counts, build requirements, the zero-package iOS + state, and the non-authorization statements. +- Nine negative mutations prove rejection of patient-data and production-access claims, a + standards overclaim, stale source hash, removed direct dependency, duplicate component, + unknown graph target, asserted iOS third-party package, and live-release approval claim. + +### Documentation and residual risk + +- Added the detailed + [foundation dependency inventory decision and evidence](../nightingale/FOUNDATION-DEPENDENCY-INVENTORY-2026-07-26.md) + and linked the generated record from the Nightingale documentation index. +- Updated the plan to record the bounded Stream A dependency-inventory exit evidence while + leaving external identifier reservation, signing, stores, live features, and release + gates open. +- Updated `THR-SC-001` to reference the generated source-hash-bound Release runtime + inventory. `RISK-007` remains open because exact-SHA CI and graph drift detection do not + provide an approved vulnerability/provenance response program. +- No patient, patient record, production database, production API, identity, grant, + session, credential, route, network permission, source adapter, disclosure, mutation, + message, notification, migration, deployment, pilot, or release state was read or + changed. +- Published the bounded inventory as commit + `60e599d90df368606f7e4ca9c5a44424570a3d6f`. Exact-SHA + [CI run 30224908508](https://github.com/sudoshi/Zephyrus/actions/runs/30224908508) + completed with all 18 jobs green and no rerun. + +## Governed Nightingale background catalog and dedicated native CI + +### Product direction and asset admission + +- Adopted the seven images supplied under `nightingale/backgrounds` as the dedicated + Nightingale background collection. They are decorative atmosphere only; no filename, + species, clinical state, pathway state, acuity, urgency, care-team action, or outcome is + exposed or inferred. +- Added `nightingale/backgrounds/backgrounds.v1.json` as the immutable machine-readable + lineage record. It retains each source filename, source SHA-256, source dimensions, and + source byte count plus the exact app-derivative path, SHA-256, dimensions, and byte count. +- Created exactly seven quality-82, optimized progressive JPEG derivatives. Sources with a + long edge above 2400 pixels were downsampled without upscaling; smaller sources retained + their dimensions. `jpegtran -copy none -optimize -progressive` removed EXIF, XMP, + Photoshop/IPTC, comments, GPS-capable application markers, and other non-pixel + application metadata. +- The committed derivative set totals 4,439,974 bytes. The approximately 33 MB source + binaries are not duplicated in this stream; their exact fingerprints remain in the + manifest. A durable source archive and applicable license/attribution record remain + mandatory before any external, pilot, marketing, store, or production distribution. + +### Cross-platform experience and accessibility contract + +- Packaged the exact same seven derivatives in both native products. Android compiles the + shared `drawable-nodpi` resource root; iOS packages the same files as loose bundle + resources through its generated Xcode project. No independently recompressed platform + variant exists. +- Aligned selection across platforms to + `floorMod(local Gregorian epoch day since 1970-01-01, 7)`. The photo remains stable + throughout a local day and maps to the same catalog entry on iOS and Android, including + mathematical wraparound for negative epoch days. The iOS implementation carries only + the local timezone into a Gregorian calendar, and a non-Gregorian device-calendar + regression test prevents identifier-dependent drift. +- Prohibited carousel, parallax, timer-driven rotation, video, autonomous pan/zoom, + clinical-event selection, and automatic motion loops. The existing short, + patient-triggered presentation transition snaps when reduced motion is effective. +- Kept the photo outside the accessibility tree and input path. “Hide decorative imagery” + removes the photo and decorative mark without removing text, headings, controls, focus + order, or actions. iOS also withholds photography under Reduce Transparency or Increased + Contrast; Android uses its supported high-contrast signal. +- Moved the product header and privacy-cover content onto strong cards. A governed + system-surface gradient separates the photo from the content layer, all readable text + remains on opaque or near-opaque surfaces, and stronger supported contrast/transparency + settings force full opacity. + +### Asset and Release enforcement + +- Added `scripts/ci/verify-nightingale-background-assets.mjs`. It parses every JPEG marker + and every progressive scan through the final end-of-image marker, verifies the exact + file set, IDs, paths, source/derivative hashes, bytes, dimensions, orientation, + no-upscale/2400-pixel bounds, progressive encoding, uniqueness, accessibility policy, + selection rule, and explicit foundation-only rights state. +- The verifier includes negative mutations for a missing catalog entry, unsubstantiated + rights approval, moving selection policy, duplicate derivative identity, binary tamper, + unexpected derivative, and metadata injected after an entropy-coded image scan. +- Wired the asset verifier into the Nightingale native product-boundary script. Added + platform Release artifact verifiers that check exact app identity/version, permission/ + network/deep-link/test-hook absence, embedded surfaces and dependencies, and the exact + seven packaged JPEG hashes. +- Added dedicated Nightingale Android and iOS CI jobs for source/governance checks, + dependency drift, Debug/Release unit tests and lint/builds, Release artifact inspection, + Android API 35 instrumentation, iOS XCTest/XCUITest, and retained verification + artifacts. This stream no longer relies on the documentation-sensitive foundation job + as its only native gate. + +### Local native verification + +- Background manifest verification and all seven fail-closed mutations passed. +- The native product boundary and XcodeGen reproducibility checks passed. +- iOS normally signed Debug build-for-testing succeeded; 9/9 XCTest unit tests and 4/4 + XCUITest journeys passed on the iPhone 16e simulator running iOS 26.3.1. The unit suite + covers exact resource loading and the shared day-index contract; the UI suite covers + positive background status, imagery suppression persistence, privacy cover, and + largest-text landscape behavior. +- An initial unsigned iOS unit invocation reproduced Keychain status `-34018` in the + protected-state canary. The new background test passed in that run. The accepted normally + signed configuration then passed all nine tests; the unsigned failure is retained as + environment evidence, not counted as a product failure or passing result. +- iOS unsigned Release build and artifact verification passed. The exact local executable/ + manifest hashes are recorded in the accessibility/layout matrix. +- Android Debug and Release unit suites each passed 8/8 from forced task execution. + `verifyNightingaleProductBoundary`, `lintDebug`, `lintRelease`, Debug assembly, unsigned + Release assembly, and the exact Release APK boundary/hash scan all passed. +- Android API 35 instrumentation passed 7/7 with no failure or skip. `FLAG_SECURE` continued + to produce a black ADB capture; capture protection was not weakened for visual review. + The semantic hierarchy exposed no background filename or species token and retained all + patient-readable text and controls. +- The inspected 1170×2532 iOS image-bearing foundation screenshot had SHA-256 + `07494f15eadc37916613fd502cc43a8ccaffc7ce2a31a1b1c9d9a51abe208272`. + It showed a supplied photo behind the strong system-surface gradient and readable cards. + This is local diagnostic evidence, not patient-advisor, rights, marketing, store, + distribution, or release approval. + +### Dependency identity and current holds + +- Regenerated the deterministic foundation dependency inventory after the Android resource + source-set and iOS resource-phase changes. Component identity remains seven direct + Android declarations, 83 Release runtime components, 457 dependency edges, zero iOS + third-party packages, and four Apple system-module imports; only the governed source + hashes changed. +- Added the comprehensive background governance/integration record and updated the master + plan, documentation index, accessibility/layout matrix, migration classification, and + threat/hazard model. The plan checklist now distinguishes completed native engineering + from open rights, human review, physical-device, signing, store, pilot, and release gates. +- No production database or patient source was accessed. No patient, principal, encounter, + grant, identity link, session, clinical projection, message, notification, migration, + deployment, pilot, or release state was created, read, or changed. +- Commit, push, and exact-SHA CI ratification remain required for this background/CI slice. + +## Offline privacy/release controls and production reference reconciliation + +### Privacy declarations and fail-closed enforcement + +- Added one exact iOS `PrivacyInfo.xcprivacy` to the Nightingale application bundle. It + declares tracking false, an empty collected-data array for the current offline + foundation, no tracking domains, and one required-reason API category: + app-local `UserDefaults` under reason `CA92.1`. It does not make a claim about an + unapproved future connected product or substitute for App Store Connect declarations. +- Added an Android Network Security Configuration with cleartext denied, only system + certificate authorities trusted, and no debug override. The manifest independently + declares `usesCleartextTraffic="false"`, retains no `INTERNET` permission, retains + backup/transfer exclusion, and references the exact policy resource. +- Extended the native product-boundary verifier, Gradle boundary task, iOS XCTest, + Android instrumentation, and both Release artifact verifiers. They now fail closed on + privacy-manifest drift, Android transport-policy drift, network permission, unapproved + network-client primitives, logging/analytics/crash primitives, tracking primitives, and + clipboard/pasteboard access. +- Added + [foundation privacy and release-control evidence](../nightingale/FOUNDATION-PRIVACY-RELEASE-CONTROLS-2026-07-26.md) + and linked it from the Nightingale index. Updated the draft threat/hazard model to 26 + implemented-control claims and ten negative self-tests. This remains engineering + evidence only; privacy/security approval, future data flows, store declarations, + signed distribution, penetration testing, and live access remain open. + +### Accepted iOS simulator and Release evidence + +- Regenerated the Xcode project and confirmed no drift. A normally signed Debug + build-for-testing passed on the iPhone 16e simulator running iOS 26.3.1 under Xcode + 26.3. +- The complete accepted XCTest run passed 10/10 unit tests and 4/4 XCUITest journeys with + no failure or skip. This includes parsing the privacy manifest from the built + application bundle rather than trusting only the repository source. +- The unsigned Release build and exact bundle verifier passed. The Release executable + SHA-256 is + `0edc91abecf0a5952db48c0a3a74e39da63d2a19b7a63bbb2d8cf222350ee521`; + the packaged privacy-manifest SHA-256 is + `b20184a87e6392f293d241c8888b0bab1774d7cce2586165e87f8e070948a32a`. +- Visually inspected the active shell and inactive privacy cover at 1170×2532. The active + image SHA-256 is + `34e123006715badf38832a4cd05f40b14951043a55a7881865d8eec215c39600`; + the cover image SHA-256 is + `f7852c9065b5d3c19847168ed978a8b360b015e1f053f5e5e5d9e6392b54be0b`. + The supplied decorative background remained subordinate to readable cards, and the + inactive cover withheld the underlying content. + +### Accepted Android emulator and Release evidence + +- Debug and Release unit suites each passed 8/8. `lintDebug`, `lintRelease`, Debug + assembly, unsigned Release assembly, the Gradle product-boundary task, and the exact + Release APK verifier passed. +- The API 35 emulator instrumentation suite passed 8/8 with no failure or skip. The + installed-package test proves the Nightingale package ID, denied `INTERNET` permission, + platform cleartext policy false, and absent backup flag. +- The first instrumentation compilation failed because the new platform-policy test used + the incorrect `android.net.NetworkSecurityPolicy` import. That attempt is not accepted + evidence. The import was corrected to `android.security.NetworkSecurityPolicy`, and the + complete 8/8 suite then passed. +- The Debug APK SHA-256 is + `576788a39e6e7122bb5994c6bdfb60b6f755e6d47d9f5e750c30f8c9aae7e778`; + the Release APK SHA-256 is + `d009d9fc72cc5cb626216e3793eebff67612a53100a6f4dae26b0c1084b9b950`. +- `FLAG_SECURE` produced an all-black 1080×2400 shell capture with SHA-256 + `c35bacdb98b522206335afa5b9baffd2e4e3352a40749bb747e469cd403af514`. + The decoded image contained zero nonblack pixels and only fully opaque alpha. The + inspected accessibility hierarchy SHA-256 is + `a84d25001b2d46ac2d21ed16946c2f632d76742d3f3ca9d22a8f1dae682aaa1c`; + required patient-safe tokens were present, while Hummingbird identity, background + filenames, patient UUIDs, and encounter UUIDs were absent. +- The compiled manifest and resource inventory plus the installed platform-policy test + prove the transport policy. A diagnostic attempt to address the compiled XML by its + source filename through `aapt2 dump xmltree` did not locate that compressed resource + path and is not treated as evidence. + +### Authorized production reference-patient audit + +- Used the separately granted production authorization only for a transaction-scoped + `BEGIN READ ONLY` audit over TLS. No credential, patient name, contact field, source + identifier, bearer material, enrollment hash, or token was printed, persisted, or added + to repository artifacts. +- The exact synthetic reference key already resolves to one active, nondeleted, + nondischarged encounter owned by the existing operational reference provisioner. + Therefore the requested sample-patient outcome already exists and a duplicate encounter + was neither needed nor safe. +- The existing command-owned identity foundation contains exactly one safe + pending/inactive principal, one verified/nonrevoked identity link, and one + pending-or-active/nonrevoked encounter grant. It also contains two enrollment challenges + that retain `issued` status but are both expired by time; zero are currently unexpired. +- Performed no production mutation, migration, provisioning command, correction, + enrollment refresh, activation, deployment, or patient disclosure. The audited records + are legacy Hummingbird reference fixtures, not a Nightingale identity/source decision. + The Nightingale migration classification rejects that provisioner as deployed product + behavior, so this audit does not authorize reuse, reissuance of enrollment material, or + patient activation. + +### Current boundary + +- The completed checklist entries cover only the bounded offline privacy carry-forward and + the read-only reconciliation of an already-existing synthetic production reference. +- Identity/provider selection, real patient linkage, current-inpatient source binding, + field-level content release, connected API contracts, messaging, notifications, + accessibility conformance, independent clinical/privacy/security review, distribution, + pilot, and production release all remain open and default denied. +- Scoped commit, push, and exact-SHA CI ratification remain required for this slice. + +## 2026-07-27 — Foundation accessibility and language readiness + +### Audit and bounded implementation + +- Audited the current iOS and Android offline shells against the remaining Stream E + language-expansion, RTL, heading, and status-announcement gaps. The audit found Android's + privacy title lacked heading semantics, status changes lacked an explicit restrained + announcement policy, visible copy was not governed through one exact cross-platform + contract, and neither native suite exercised text expansion or RTL. +- Added an exact 15-key English source contract in iOS `Localizable.xcstrings` and Android + `res/values/strings.xml`. Every visible foundation string now resolves through the + native localization system. The new self-testing verifier rejects missing/extra/drifted + copy, runtime literals, unapproved iOS locales, Release pseudolocales, missing headings, + assertive Android announcements, or missing pseudolanguage journeys. +- Exposed exactly three ordered headings on both platforms. Android's privacy heading now + carries Compose heading semantics. +- Added exactly two restrained status-notification paths. Android uses polite live-region + semantics. iOS posts a localized, low-priority `UIAccessibility` announcement only + after the user changes reduced motion or decorative imagery. +- Added a compile-time Debug-only iOS RTL layout adapter and build-type-specific Android + language-readiness policy. Android Debug maps only the `ar-XB` pseudolocale to RTL; + Android Release always uses the platform direction and contains no `ar-XB` literal. + Pseudolocales remain tests, not translations. + +### Accepted native and artifact evidence + +- The signed iOS Debug unit suite passed 11/11 on the iPhone 16e simulator running iOS + 26.3.1 under Xcode 26.3. +- The complete iOS UI suite passed 6/6. The added double-length journey proved rendered + copy expansion, landscape reflow, 44-point controls, and both state changes. The RTL + journey proved a heading moved from left-leading to right-leading alignment, retained + the exact five-element semantic order, and preserved an operable mirrored switch. +- The iOS unsigned Release build and exact verifier passed. The Release application + contains exactly `en.lproj`, its compiled strings parse to all 15 exact values, and no + test hook or pseudolocale is present. The Release executable SHA-256 is + `3667f3c743ec7b5ddce915beef2f0c96ead628d2d166d563de2f7e6bf3a1b698`. +- Android Debug and Release unit suites each passed 8/8. `lintDebug`, `lintRelease`, Debug + assembly, unsigned Release assembly, and the exact Release APK verifier passed. +- The complete Android API 35 emulator instrumentation suite passed 10/10. The new journey + proved `en-XA` expansion, `ar-XB` bidirectional markers, RTL direction on the actual + tagged shell, exact semantic order, and a reachable/operable switch. The Debug APK + contains `en-rXA` and `ar-rXB`; the Release APK contains neither. +- The Android Release APK SHA-256 is + `28b055b8873f8db9cf98a24edacdbf74349fbb6c6fd013175df12bca4e7f673e`. + `FLAG_SECURE` retained an all-black capture. The non-PHI `en-XA`/`ar-XB` + accessibility hierarchies and iOS visual captures are retained under + `docs/evidence/nightingale/accessibility-language-readiness-2026-07-27/`. +- Regenerated the dependency inventory because iOS now imports the Apple UIKit system + module for accessibility announcements and Android's build source changed. It retains + seven direct Android declarations, 83 resolved components, 457 edges, zero iOS + third-party packages, and now records five Apple system modules. + +### Corrected failures retained + +- The first Android instrumentation compilation used unavailable + `SemanticsConfiguration.getOrNull`; `getOrElseNullable` is the accepted implementation. +- The first iOS compilation proved SwiftUI did not expose the attempted + `accessibilityLiveRegion` modifier. The accepted low-priority UIKit announcement policy + compiled and passed the complete suite. +- Apple locale/text-direction launch arguments alone did not mirror the English-only + SwiftUI bundle. The accepted Debug-only direction adapter produced changed geometry and + is absent from Release behavior. +- The first iOS RTL switch assertion assumed a clean persisted state. The accepted test + records the pre-tap value and proves an actual change. +- Android's first locale cleanup waited for an empty resource locale even though system + fallback is nonempty. The accepted helper separately validates app locale selection and + nonempty resource selection. +- The first Android `ar-XB` run rendered bidirectional markers but left the Compose tagged + shell LTR under instrumentation. The Debug-only policy now mirrors the actual shell; + the Release implementation remains platform-directed. +- The first Android direction assertion inspected the instrumentation framework's outer + root rather than Nightingale's tagged shell. The accepted assertion targets + `nightingale-safe-shell`. +- One intermediate Compose edit omitted an outer brace and failed both Debug and Release + compilation. It was corrected before the full unit, lint, assembly, instrumentation, + and artifact runs. None of these failed preflights is accepted as passing evidence. + +### Checklist, threat model, and remaining boundary + +- Checked only the new bounded accessibility/language-readiness subitem and immediate + sequence item 20. The broad WCAG/platform-guidance item and every named human-review + item remain unchecked. +- Added `CTRL-027` to the draft threat/hazard model and increased its verifier to 27 exact + controls plus 11 negative self-tests. The new control explicitly withholds translation, + human assistive-technology, future-screen, and WCAG claims. +- Added the full + [accessibility/language-readiness evidence](../nightingale/FOUNDATION-ACCESSIBILITY-LANGUAGE-READINESS-2026-07-27.md) + and linked the retained screenshots/hierarchies from the Nightingale index. +- No clinical or patient-data UI, route, identity/provider, source adapter, network client, + database query, disclosure, mutation, message, notification, enrollment, migration, + deployment, pilot, or production activation was added or enabled. +- The already-existing synthetic production reference outcome remains unchanged. This + slice performed no production database access or mutation. +- Scoped commit, push, and exact-SHA CI ratification remain required for this slice. + +## 2026-07-27 — Current-main integration and independent iOS release-boundary correction + +### Exact-SHA failure and source-ledger revalidation + +- Exact-SHA CI run `30278248743` passed 19 of 20 jobs and failed only the Nightingale + contract-foundation job. The exact failure was a stale `routes/api.php` digest in the + evidence-only communication/notification source ledger; all native iOS and Android jobs + passed. +- Synchronized the isolated Nightingale feature stream with `origin/main` commit + `b6ea087747d7ea88c8a076f06f4c91a2636ea029`. The streams had no overlapping changed + paths, and the merge completed without conflict. +- Revalidated every one of the 130 classified communication/notification source paths + against that exact main commit. All paths match it. Only `routes/api.php` changed from + the prior evidence snapshot. +- Inspected the twelve-line route delta. It adds three Zephyrus staff/operations reads: + scoped Arena per-case conformance, an aggregate patient-flow epoch, and a scoped + patient-flow journey. It adds no Nightingale route, communication operation, + notification provider, patient-native transport, or communication state. +- Reissued the generated ledger with the current route digest and advanced its reviewed + source commit/date. The `routes_rejected` disposition, all ten required findings, all + 17 decisions, and every default-deny constraint remain unchanged. + +### Legacy release-path collision found and corrected + +- The deeper product-universe pass found a material mainline regression: the historical + `hummingbird/iosPatientApp` project, generated project, and Info.plist had been renamed + to the Nightingale bundle/display identity, and a Nightingale App Store export profile + had been placed inside that legacy root. This created two competing Nightingale iOS + sources and contradicted the independent-product decision. +- Correction commit `85316fbc5794735a77a0a4fa0e0096e18db4240b` restores the three + legacy identity-bearing files to their exact pre-collision Hummingbird Patient bytes. + The Nightingale export policy now lives under `nightingale/iosApp`; the non-secret Apple + registry points only to `nightingale/iosApp`, `Nightingale.xcodeproj`, and the + `Nightingale` scheme. +- Added a fail-closed IPA identity verifier. It reads an IPA without extraction, rejects + unsafe paths or multiple top-level applications, and requires the expected bundle ID, + build number, marketing version, and `APPL` package type. Its self-test passes one + positive and rejects wrong-bundle, wrong-build, duplicate-app, and malformed-archive + mutations. +- The root TestFlight helper now requires a registry bundle ID, decimal-only build number, + an exact safe export-cleanup target, and a verified exported IPA before it can report a + successful export. No archive, signature, upload, App Store query, tester distribution, + or pilot action was performed. +- Product-boundary CI now rejects legacy Nightingale identity, a misplaced export profile, + registry reversal, export-policy drift, missing independent distribution documentation, + or IPA-verifier failure. The generated legacy Xcode project, mobile brand surfaces, + product boundary, plist parsing, helper syntax, and Nightingale Release build settings + all pass locally. + +### Corrected universe and dependent evidence + +- The only new tracked source retained under the legacy patient roots is + `hummingbird/iosPatientApp/.gitignore`. It is classified as a reusable generated-artifact + hygiene principle, not product behavior. The complete migration universe is therefore + 256 paths: 122 unique prior-ledger sources plus 134 final-slice sources. +- The full-universe digest is + `a307e1957df7ef78eb61a9a9123f3902fd8929ebb3aaeb4dce48f2c88fb4a881`; + the final-slice path digest is + `4301de88a9071214001c2a58aa8fbc624bb49f24bb566428c8a1ef98aa44c13d`. + The final slice contains 42 reusable safety primitives, 20 held product behaviors, 28 + test/fixture-only sources, and 44 rejected legacy behaviors. +- Reissued the dependent, still-held Today evidence with the corrected universe identity. + Its 68 synthetic outcomes, 24 adversarial mutations, 14 direct-source checksums, empty + executable contract, and all activation=false conditions remain unchanged. +- Checked only the new mainline-collision correction and revalidation checklist section. + The broad signed-distribution/store/upgrade item remains open, as do every live identity, + source, clinical content, communication, notification, independent review, pilot, and + release gate. + +### Current-main native reacceptance and lifecycle correction + +- Re-ran the signed Nightingale iOS Debug suite on the iPhone 16e simulator running iOS + 26.3.1. All 11 unit tests and all six UI journeys passed, including double-length text, + accessibility text in landscape, Debug-only RTL, display preferences, the offline + privacy message, and the lifecycle privacy cover. +- Built the independent Nightingale iOS target in unsigned Release configuration and + passed the exact Release-artifact verifier. Its bundle identifier is + `net.acumenus.nightingale`; its executable SHA-256 is + `a747b4efbfdd33e94c39fa996e76f9798ed51232e4412c57e1798b48394615f9`. +- Independently built the restored legacy Hummingbird Patient iOS target in unsigned + Release configuration and passed its patient-transport artifact verifier. Its bundle + identifier is `net.acumenus.hummingbird.patient`; its executable SHA-256 is + `098e92a7be150ad25c3572c4ea1c0976e218dc76ec1fff0105551c9f03c1bdeb`. + This proves the two Release products no longer collide; it does not approve either + artifact for signing or distribution. +- Android Debug and Release unit suites each passed 8/8. `lintDebug`, `lintRelease`, + Debug assembly, unsigned Release assembly, and the exact Release APK verifier passed + under Android Studio's bundled Java 21 runtime. The Debug APK SHA-256 is + `6511676a6ce092b2a868b1bc6f28027f7a60dd13f27da1e994a74473fdab2b47`; + the unsigned Release APK SHA-256 remains + `28b055b8873f8db9cf98a24edacdbf74349fbb6c6fd013175df12bca4e7f673e`. +- The first complete Android emulator run passed nine of ten journeys but exposed a + harness race: changing `LocaleManager.applicationLocales` automatically destroyed and + recreated the Activity while the helper also called `ActivityScenario.recreate()`. + The helper now waits for the platform-created replacement Activity to reach `RESUMED` + with the requested resource locale instead of issuing a competing recreate. The failed + case then passed alone, and the complete API 35 suite passed 10/10. +- The first Gradle preflight used the shell's Java 8 runtime and failed before project + configuration. It is not accepted evidence. The complete accepted command used Android + Studio's Java 21 runtime and passed all requested unit, lint, assembly, and emulator + tasks. +- Test correction commit `45ddf907c0f15e378b37a1b0726724e346cb29fd` is the new exact + reviewed source commit for the final 134-source classification. The product-universe + and final-slice path sets and digests are unchanged; the changed Android test source + byte checksum and dependent Today direct-source evidence are regenerated from that + commit. +- No production database access, mutation, migration, provisioning, deployment, patient + disclosure, live identity/source binding, networking, message, notification, or + activation occurred in this reacceptance slice. Exact-SHA CI remains required after the + scoped evidence commit is published. + +## 2026-07-27 — Cross-surface patient-journey reference scenarios + +### Reconciliation and bounded decision + +- Reconciled the master plan's required patient scenarios against the held encounter, + identity/session/recovery, current-inpatient-source, Today, communication/notification, + and complete migration-classification evidence. This was a repository-only review; no + production database, deployed service, patient, representative, clinician, message, + source response, or runtime configuration was accessed. +- Added a held, non-runnable candidate covering exactly 15 families through 27 synthetic + no-PHI cases: admission; transfer; procedure; test delay and result release; pre-round + question and released response; shift handoff; changed discharge estimate; discharge + with an open thread; identity correction; representative invitation/scope/expiry/ + revocation; language and interpreter support; visual/hearing/motor/cognitive/low-literacy + accommodations; sensitive-data denial; source outage/staleness; and content retraction/ + correction. +- The catalog makes cross-surface transition expectations explicit without approving + patient functionality. Source events do not become patient releases; server acceptance + does not become staff delivery/read/review; representative and sensitive-resource cases + remain fully withheld; outages do not become empty care plans; stale display is + field-policy-specific; and retraction/correction propagates atomically. +- Every case requires the same 13 global approval gates plus a scenario-specific approval. + Every response remains separate-release-gated, atomic, no-store, content-free-audited, + inaccessible to offline PHI or queued mutation, and bound to kill-switch/release- + withdrawal/cache-handle-draft purge requirements. + +### Reproducible artifacts and enforcement + +- Added deterministic builder + `scripts/ci/build-nightingale-patient-journey-reference-candidate.mjs`, which emits the + candidate and fixtures under + `docs/nightingale/api-contract/candidates/patient-journeys/v0/`. +- Bound the candidate to 12 exact source artifacts at reviewed commit + `a825db89ec1efaf4b2c55a26e04d41161445b001`. The source inventory digest is + `aed369c71dd22596ed09c908746e529617c3a62e1c92cfea5cb595895245ffea`; + the canonical fixture digest is + `525c6f9264a14687b906485fcd105dd689c41e207785355ebf4f31a38f569de9`. +- Added an independent verifier that rejects an executable path or activation, any of 19 + runtime/production permissions becoming true, family/case drift, a runnable or + non-synthetic fixture, missing release/no-store/approval controls, representative or + sensitive disclosure, concrete identifiers, endpoint exposure, delivery-state + overstatement, offline PHI/queue/stale-current behavior, accessibility information/action + loss, weakened identity/outage/retraction behavior, source drift, or fixture drift. +- The verifier reproduces both artifacts from the deterministic builder, passes the + canonical record, and rejects all 23 adversarial mutations. It is + now part of the existing Nightingale contract-foundation CI job. +- The initial verifier run correctly rejected an over-broad assumption that every value + under `x-nightingale-activation` is Boolean; the foundation also contains the exact + string `default: disabled` and an approval-list array. The accepted verifier explicitly + checks the nine activation Booleans plus the disabled default. +- The next run rejected the admission case because it had only the global approval set. + The accepted fixture now also requires an explicit admission-context and first-release + policy. Neither failed preflight is accepted as passing evidence. + +### Checklist and remaining boundary + +- Checked only the existing Stream D reference-scenario item. The broad implementation + item for Today, My Path, Care Team, education/teach-back, discharge, and communication + remains unchecked, as do generic unknown/revoked/cross-principal proof, all named + approvals, production-like integration, pilot, distribution, and deployment. +- The executable Nightingale contract still contains zero paths. All route, controller, + provider, client, native networking, identity-provider, source-adapter, database query, + clinical release, communication mutation, notification, representative creation, + production, migration, deployment, and pilot permissions remain false. +- Exact-SHA CI ratification remains required after this scoped candidate slice is + committed and pushed. + +### Exact-SHA security preflight correction + +- Exact-SHA CI run `30288424956` passed the Nightingale contract-foundation job, + including the new catalog and all 23 negative self-tests, but the history secret scan + rejected one line of this devlog before the overall run could qualify as release + evidence. +- The retained redacted scanner report identifies rule `generic-api-key`, this document, + line 1917, and commit `e72abafaaba728ebb8d7aedde154c3086d6a09b9`. Inspection of the + source and redacted evidence confirmed that adjacent ordinary prose was parsed as a + key/value construction and that the captured value was the non-secret prose fragment + `notification/delivery`; no credential, token, endpoint, patient datum, or production + value was present. +- Rephrased the live document to remove the ambiguous construction and added an + AND-scoped history exception for only this exact document and captured word. No path + wildcard, rule disablement, credential-shaped regex, or production-secret exception was + introduced. The failed run remains failed evidence; a new exact-SHA history and + working-tree scan is required. + +## 2026-07-27 — Route-free generic non-disclosure foundation + +### Bounded implementation + +- Added Nightingale-owned relationship, current-context-binding, resource-release, and + disclosure-disposition vocabularies plus a pure generic non-disclosure gate under + `app/Nightingale/Disclosure`. +- The gate accepts no identifier or patient value and has no framework, route, controller, + provider, source, database, audit-writer, or native caller. It cannot activate or + disclose anything. +- Unknown, revoked, expired, cross-principal, wrong-encounter, omitted-resource, and failed + upstream-precondition states all produce the exact same public failure tuple: status + `404`, code `not_found`, and cache policy `private, no-store, max-age=0`. There is no + message, internal reason, identifier, redirect, or retry field. +- Only the all-positive combination of prerequisite continuation, active relationship, + matching current context, and released resource returns + `continue_to_governed_projection_evaluation`. That disposition is explicitly weaker than + authorization or clinical release. + +### Independent proof + +- Expanded the backend PHPUnit foundation to 21 tests and 111 assertions. Seven named + cases directly cover the master-plan requirement, and the exhaustive test evaluates all + 40 combinations: one continues and 39 withhold. +- Expanded the dependency-free backend verifier to pin the exact state vocabularies, + public tuple, one/39 cardinality, and runtime non-registration. It repeats the 40-row + truth table independently of PHPUnit and remains part of the existing contract CI job. +- Added + `docs/nightingale/GENERIC-NON-DISCLOSURE-FOUNDATION-2026-07-27.md` with the evaluation + matrix, audit boundary, runtime limitations, evidence inventory, verification commands, + and unresolved operation-specific requirements. +- Checked only the Stream D generic non-disclosure item. Operation-specific identity, + source, authorization, audit, timing-equivalence, HTTP-edge, clinical/content, + language, native-client, reviewer, pilot, and production requirements remain open. +- No production database access, mutation, migration, patient creation, route, identity or + source binding, patient disclosure, network client, clinical content, communication, + notification, representative access, activation, distribution, or deployment occurred + in this implementation slice. + +### Native regression reacceptance + +- Booted the iPhone 16e simulator on iOS 26.3.1 and ran the complete signed Debug test + scheme from a fresh DerivedData directory. All 11 Nightingale unit tests and all six + UI journeys passed with zero failures or skips, including persistence, double-length + reflow, largest-text landscape, foundation copy, lifecycle privacy cover, and + right-to-left layout. +- Cold-booted the wiped `hb` Android 15/API 35 AVD without a snapshot. The accepted Java + 21/Android SDK invocation reran all eight Debug JVM tests, all eight Release JVM tests, + and all ten installed instrumentation journeys with zero failures, errors, or skips. +- The first Android invocation was not accepted evidence because the isolated shell did not + export `ANDROID_HOME`; Gradle stopped before dependency resolution or test execution. + The accepted invocation explicitly pinned Android Studio Java 21, `ANDROID_HOME`, and + `ANDROID_SDK_ROOT`. +- Both emulators were shut down after result-XML reconciliation. This native pass proves + that the backend-only non-disclosure slice did not regress the current offline products; + it does not make the new backend sample reachable from either app. + +## 2026-07-27 — Authorized Nightingale production sample clone + +### Direction change and source preservation + +- The prior production audit had deliberately remained read-only because the plan then + prohibited a Nightingale production sample. The operator subsequently gave an explicit + direction to create the Nightingale sample and to use the deprecated Hummingbird Patient + reference patient as its source template. +- Preserved the Hummingbird operational encounter, principal, identity link, grant, and + expired enrollment-challenge records unchanged. No Hummingbird credential, challenge, + external UUID, keyed digest, encrypted source reference, projection content, or + product-owner value was copied. +- Cloned only the safe synthetic shape: patient principal type, `en-US` locale, + `America/New_York` timezone, medical/surgical unit 85, no bed, acuity 2, and an active + operational/not-discharged state. + +### Transactional creation + +- The first serializable, advisory-locked transaction created exactly one + `prod.encounters` row with patient reference `demo-nightingale-reference-inpatient` and + owner `nightingale-reference-patient-provisioner-v1`. Exact zero-cardinality, owner + collision, unit availability, lock-timeout, statement-timeout, constraint, and + pre-commit row checks were enforced. +- The second serializable transaction selected exactly one safe Hummingbird template + principal, generated a fresh UUIDv7, and created exactly one principal with display name + `Nightingale Reference Patient`. It is synthetic, product-tagged `nightingale`, pending, + inactive, and has no email, phone, password, verification timestamp, or authentication + history. +- A fresh read-only reconciliation proved one exact operational row and one exact + principal, with zero Nightingale identity links, encounter grants, enrollment + challenges, sessions, access-audit events, and notification devices. It also reproved + the original Hummingbird encounter/principal/identity/grant one/one/one/one + cardinalities. + +### Boundary + +- The sample cannot log in or reach patient data. It has no database-level identity/ + encounter association, no projection, no route, no source adapter, and no native caller. + Nightingale's executable contract remains empty and both native applications remain + offline. +- No migration, clinical content, representative record, message, notification, feature + activation, pilot enrollment, application deployment, or teardown occurred. +- The complete secret-free preflight, transaction, reconciliation, residual-risk, and + follow-up record is + `docs/evidence/nightingale/production-sample-patient-2026-07-27/README.md`. + +### Direction-confirmation reconciliation + +- After the operator reiterated that Nightingale must use the deprecated Hummingbird + Patient sample, reran the production proof in a repeatable-read, read-only transaction. + It reconfirmed the exact one/one Nightingale encounter/principal result; zero + Nightingale identity links, grants, challenges, sessions, audit events, and notification + devices; and the unchanged one/one/one/one Hummingbird + encounter/principal/identity-link/grant result. +- Corrected the evidence vocabulary to name the exact lineage keys: + `preferences.provisioning.source_template_product=hummingbird_patient` and + `preferences.provisioning.source_template_owner=hummingbird-patient-reference-identity-provisioner-v1`. + A first metadata predicate used a nonexistent generic `source_template` path; the + follow-up isolated that query mistake and proved the stored sample itself was correct. +- No duplicate sample, identity binding, encounter grant, content projection, route, + activation, migration, deployment, or other production write was required or performed. + +## 2026-07-27 — Current-main reconciliation at `a979e786` + +### Upstream merge and fail-closed evidence refresh + +- Fetched and merged `origin/main` at + `a979e78630c214f6f77d96ce702227da6c03a9b6` without conflicts. The upstream slice + contains the Flow 4D per-patient conformance surface and iOS CI build-chain + improvements; it does not add a Nightingale route or native patient capability. +- The Nightingale contract chain correctly stopped at the 130-file + communication/notification ledger because `routes/api.php` had changed. The five + preceding contract, encounter, Today, journey, and identity/source verifiers all + passed before that fail-closed stop. +- Reviewed the exact 15-addition/five-deletion route delta. It gates the existing staff + Arena conformance read and adds a staff-only scene read plus a governed Eddy + exception-note proposal. These are staff operations behind Arena, Flow 4D, and + scoped-patient controls; they add no Nightingale patient communication, notification, + client transport, delivery-state claim, or migration permission. +- Extended the generated ledger with an exact source-revalidation record containing the + upstream commit, previous/current SHA-256 values, line counts, unchanged + classification impact, and bounded rationale. The verifier now rejects commit, date, + source, digest, line-count, impact, and rationale drift and includes a negative + mutation that attempts to promote the impact to `approved`. +- This reconciliation changes no Nightingale checklist item, product behavior, route, + source adapter, identity binding, content release, production record, native client, + or activation state. + +## 2026-07-27 — Namespace closure and activation-state separation + +### Implementation + +- Added a deterministic Nightingale namespace manifest covering 12 exact native source + files, ten shared accessibility IDs, four iOS Debug hooks, ten fully qualified + persistent identifiers, zero telemetry events, and zero diagnostic channels. +- Fully qualified the Android presentation keys and both platforms' future-session-binding + identifiers under `net.acumenus.nightingale`. Strengthened native tests and the + product-boundary scanner so short or legacy identifiers fail closed. +- Added an independent manifest verifier that reproduces builder output byte-for-byte, + validates source SHA-256 values, rejects legacy Hummingbird Patient tokens and + unregistered logging/analytics primitives, and exercises eight negative mutations. + Contract CI and both native product-boundary jobs invoke it. +- Added separate PHP state types for institutional clinical approval, patient-content + release, product feature activation, pilot enrollment, and source-connector deployment. + All five default to negative states in code-owned configuration without environment + hooks. +- Added a route-free conjunctive gate. The independent verifier and PHPUnit enumerate all + 32 combinations: 31 return `hold`; only the all-positive row returns + `continue_to_operation_specific_release_evaluation`. That continuation is explicitly not + identity, disclosure, production, clinical release, or pilot authorization. +- Extended the empty executable contract and all dependent evidence verifiers with five + separate false activation facts. Regenerated the held patient-journey candidate after + its exact contract-source checksum changed. No route, provider binding, source query, + patient content, message, notification, telemetry, diagnostic channel, migration, or + deployment was added. + +### Backend and contract acceptance + +- The focused backend suite passed 23 tests and 149 assertions. +- The complete Nightingale contract chain passed, including the empty contract, encounter + candidate, Today candidate, 15-family/27-case patient-journey catalog, 64-case + identity/source decisions, 65-source identity ledger, 130-source communication ledger, + 134-source journey ledger, threat/hazard model, dependency inventory, namespace + manifest, backend verifier, and native product boundary. +- The namespace manifest reproduced exactly and all eight negative mutations failed as + required. The activation proof retained exactly one limited continuation and 31 holds. + +### Native acceptance + +- Rebuilt from isolated `/tmp` roots. Android passed eight Debug and eight Release unit + tests, Debug/Release lint, Debug/Release assembly, and the unsigned Release APK boundary + verifier with zero test failures, errors, or skips. +- Ran a fresh iOS build-for-testing and the iPhone 16e simulator on iOS 26.3.1. The + xcresult summaries recorded 11/11 unit tests and 6/6 UI journeys, with zero failures or + skips. The unsigned Release application and exact binary boundary verifier also passed. +- The shared `hb` Android AVD was already occupied by a separate Claude process. Preserved + that process and instead cold-booted a wiped, isolated `nightingale-codex` Android + 15/API 35 AVD on port 5556. All 10/10 installed journeys passed with zero failures, + errors, or skips. Shut down only the isolated AVD and the Nightingale iPhone simulator; + the pre-existing Android emulator remained untouched. + +### Checklist result and residual boundary + +- Checked the bounded Stream C namespace-reissue item and Stream D + approval/release/activation/enrollment/connector-separation item. The master checklist + now records 41 of 54 items complete, or 75.93%. +- The remaining 13 items continue to require live operation contracts and implementations, + approved identity/source/content/reviewer decisions, signed-distribution evidence, + human validation, integration exercises, or pilot authorization. The sample patient + remains pending/inactive and unreachable. + +## 2026-07-27 — Current-main duplicate-identity reconciliation + +### Collision and resolution + +- Fetched `origin/main` at `ea14d2bd2be9656555dfba35da2dbf28e74730e3`. + It included unrelated Flow 4D plan commit `f63e5994` and PR #101, which renamed the + deprecated Hummingbird Patient native/backend/contract surfaces to Nightingale. +- Merged the complete upstream graph at `bcaae3da`; then forward-reverted only PR #101 at + `c11d462c`. This preserves the unrelated Flow 4D change and published history while + preventing two native roots and two backend configurations from claiming one + Nightingale identity. +- Resolved all overlapping paths against the exact previously accepted + `21e4de17e27c258955c0b14ba490f9d0d4be16b5` tree. The boundary-restoration tree at + `c11d462c` differs from that baseline only in the Flow 4D plan, and that path is + byte-identical to `f63e5994`. +- Reconfirmed the canonical split: `nightingale/iosApp` and + `nightingale/androidApp` own `net.acumenus.nightingale`; the deprecated + `hummingbird/*PatientApp` roots retain `net.acumenus.hummingbird.patient`. + `config/nightingale.php` remains route-free and inert; the legacy Hummingbird Patient + configuration/contract remains migration input. + +### Complete reacceptance + +- The 13-stage contract, candidate, classification, threat/hazard, dependency, namespace, + backend, and native-boundary chain passed, including all negative self-tests and exact + 256-source predecessor coverage. +- The focused Laravel suite passed 23 tests and 149 assertions using a worktree-local + dependency tree whose `composer.lock` SHA-256 matched the canonical checkout. +- On a freshly booted iPhone 16e simulator running iOS 26.3.1, Nightingale passed 11/11 + unit tests and 6/6 UI journeys with no failures or skips. The unsigned Release + application passed the complete artifact boundary. +- Android passed 8/8 Debug and 8/8 Release unit tests after forced uncached execution, + both lint variants, both assemblies, and the unsigned Release APK boundary. +- Preserved the other process's API 35 emulator on port 5554. A separate read-only API 35 + instance on port 5556 passed 10/10 installed journeys with zero failures, errors, or + skips, then only that instance was shut down. The iOS simulator was also shut down. + +### Checklist and safety accounting + +- Added the exact reconciliation record under + `docs/evidence/nightingale/current-main-reconciliation-2026-07-27/`. +- No new functional checkbox is credited. This restores an already-completed boundary, so + the master checklist remains 41/54 (75.93%) with all 13 approval, implementation, + distribution, human-review, integration, and pilot items open. +- No production database access, sample-patient mutation, route, source query, identity + provider, content release, notification, signing, upload, deployment, or activation + occurred. The authorized Nightingale sample remains pending/inactive and unreachable. + +## 2026-07-27 — Default-off controlled-pilot manifest foundation + +### Configuration and governance boundary + +- Added a generated, non-runnable controlled-pilot candidate covering the exact + facility, unit, cohort, language, exclusion, support-hour, validity, prerequisite, + named-approval, audit, rollback, and kill-switch fields required before an external + pilot go/no-go review. +- Kept the committed template empty and fail-closed: revision zero, draft/inactive, no + facility/unit/cohort/language/support values, zero allowed enrollment, no validity + window, no prerequisite/approval records, an engaged kill switch, and no review request + or runtime activation. +- Defined opaque `ngf_`, `ngu_`, and `ngc_` handle classes; separate released + inclusion/exclusion policies; a 25-enrollment technical ceiling; prohibited automatic + enrollment; canonical locale tags with released interpreter coverage; and withhold on + unknown language or unavailable eligibility/exclusion inputs. +- Required IANA-style timezone configuration, non-overlapping local weekly support + windows, separately released urgent-help guidance, and a fixed uncovered-hours + disposition that withholds new enrollment without exposing staffing availability. +- Required exact UTC start/expiry timestamps, a maximum validity of 168 hours, explicit + fail-closed expiry, and a new manifest for renewal. The seven-day ceiling is a technical + authorization lifetime rather than a patient length-of-stay or pilot-duration claim. +- Required eight prerequisite records and seven distinct named approval roles. The + candidate records no real approver and does not appoint one. +- Required nine append-only, durable-before-change audit events with nine content-free + fields. Patient identifiers, clinical values, and message bodies are prohibited; audit + failure holds. +- Required a default-engaged kill switch, exact rollback target and verification records, + and no enrollment or disclosure after expiry. No kill-switch or rollback service was + implemented. + +### Mechanical acceptance + +- Added a deterministic builder and an independent verifier. The verifier reproduces both + JSON artifacts exactly, binds four foundation/activation sources by SHA-256, requires + the executable contract to retain zero paths, and requires every executable activation + fact plus every backend production/disclosure/mutation default to remain negative. +- Evaluated 34 synthetic no-PHI cases. Exactly 33 hold. The single complete synthetic + case may proceed only to `eligible_for_external_go_no_go_review_only`, which is + expressly not activation, enrollment, deployment, disclosure, clinical release, or + production authorization. +- Exercised 25 adversarial artifact mutations. Corrected the self-test harness during + implementation so a mutation counts only if validation itself rejects it; a synthetic + “did not fail” assertion can no longer be mistaken for a passing negative test. +- Integrated the verifier into the docs-sensitive Nightingale contract CI job and the + native product-boundary chain. +- The full 14-stage Nightingale contract/backend/native-boundary chain passed. The + focused Laravel suite remained 23/23 tests and 149 assertions. + +### Native emulator reacceptance + +- Booted an isolated iPhone 16e simulator on iOS 26.3.1. The generated Xcode project + matched `project.yml`; 11/11 unit tests and 6/6 UI journeys passed with zero failures or + skips. The unsigned Release Simulator application passed the exact identity, privacy, + dependency, no-network/no-deep-link/no-test-hook, English-copy, and background boundary. + The isolated simulator was shut down. +- Preserved the other process's API 35 Android emulator on port 5554. Started a second + read-only `hb` instance on port 5556. Both lint variants, both assemblies, and the + unsigned Release APK boundary passed; all 10/10 installed journeys passed with zero + failures, errors, or skips. +- The first Android JVM invocation restored cached outputs and was not accepted as fresh + execution. A forced `--rerun-tasks` pass executed all 45 actionable tasks and passed + 8/8 Debug plus 8/8 Release tests with zero failures, errors, or skips. +- Terminated only the port-5556 Android instance. The pre-existing port-5554 emulator + remained connected. + +### Checklist and safety accounting + +- Checked the first Stream F item and added immediate-sequence milestone 22. Exact + checklist reconciliation is now 42 checked, 12 open, 54 total: **77.78% complete**. +- Added the decision record under + `docs/nightingale/CONTROLLED-PILOT-MANIFEST-FOUNDATION-2026-07-27.md` and exact + acceptance evidence under + `docs/evidence/nightingale/controlled-pilot-manifest-2026-07-27/`. +- No production database access, sample-patient change, real pilot scope, identity/source + selection, patient operation, enrollment, content release, audit sink, route, client, + signing, distribution, deployment, or activation occurred. The sample remains + pending/inactive and unreachable. + +### Current-main reconciliation + +- Committed the validated controlled-pilot slice as + `487d1c796fd9359819d501ccb2d3c1c60928b7d9`, then merged current `origin/main` + `996a5336066e0523de63c3e88f7bc0e1243ab192`. +- Confirmed the upstream delta changes only `.github/workflows/ci.yml`, adding the + independent `frontend-build` job and routing Browser/DAST through it. +- Confirmed the automatic merge retained the controlled-pilot verifier in the + Nightingale contract job. No runtime, contract, sample-patient, native-client, or + database source changed after emulator acceptance. +- During exact-SHA CI, `origin/main` advanced to documentation-only commit + `98d9bf98f2e811f6b39c0d14bb5e0ea1b5ea7317`; merged it without runtime changes. +- Investigated the resulting Security failure with the pinned Gitleaks 8.28 evidence. The + new upstream devlog repeated the same ordinary `notification/delivery` prose phrase + already narrowly excepted for the Nightingale devlog. Extended that `AND`-scoped + allowlist only to the exact new document path and exact phrase. +- Verified the fix with the checksum-verified pinned 8.28 macOS binary: all 1,082 locally + visible commits scanned with zero findings. The five working-directory findings were + confined to ignored Android build/test output and are absent from a clean checkout; + they were not treated as repository findings. + +## 2026-07-27 — Fail-closed background rights and source-archive review + +### Evidence reconciliation + +- Replaced the background distribution hold's single prose assertion with a separate, + immutable v0 review bound to the exact seven catalog IDs, source filenames, source + dimensions, source SHA-256 values, and catalog-file digest. +- Applied a deliberately narrow evidence standard: filenames and photographer profiles + are not source or rights evidence; live provider pages require asset-level + reconciliation; live license pages are observations rather than durable terms + snapshots; local workstation paths are not durable archives; and committed optimized + derivatives are not original archives. +- Resolved the filename-embedded Unsplash IDs for catalog assets 05 and 06 to their exact + official pages. Recorded creator, canonical URL, publication timestamp, provider + dimensions, current download hash/dimensions, checked date, and the provider's current + license/terms URLs. +- Proved asset 06's current provider download is byte-for-byte identical to the catalog + source. For asset 05, reconciled the current 2608x3912 provider source to the supplied + 2400x3600 encoding with normalized RMSE `0.0112669` and perceptual-hash distance + `0.0247332`; classified it only as the same resized/re-encoded provider asset. +- Left assets 01, 02, 03, 04, and 07 unresolved. Search results, visually similar images, + generic wallpaper pages, and inferred stock providers were not promoted to evidence. +- Recorded zero durable source archives, zero durable license snapshots, zero + rights-cleared assets, zero release-owner approvals, and zero distribution-eligible + assets. No legal determination was made. + +### Fail-closed control + +- Added `nightingale/backgrounds/rights/rights-review.v0.json` and linked it from the + governed catalog without weakening the existing foundation-only distribution state. +- Added an independent rights verifier to both the Nightingale contract CI job and the + native product-boundary chain. It performs no network request and validates the frozen + v0 observation rather than treating volatile provider state as a release decision. +- The verifier reconciles every row to catalog lineage, requires the exact two identified + provider records and five unresolved records, holds every archive/approval/eligibility + field empty or false, and checks summary counts. +- Twenty adversarial mutations failed closed, covering invented distribution/legal/ + release-owner approval, automated release, missing/duplicate assets, source and catalog + hash drift, guessed provider identity, false binary equivalence, local-path archive + promotion, invented terms evidence, rights/eligibility count inflation, and + filename-only rights evidence. +- Added the detailed decision record under + `docs/nightingale/BACKGROUND-RIGHTS-AND-SOURCE-ARCHIVE-REVIEW-2026-07-27.md` and exact + acceptance evidence under + `docs/evidence/nightingale/background-rights-review-2026-07-27/`. + +### Complete regression acceptance + +- The expanded 15-stage contract/governance/backend/native-boundary chain passed, + including the new 7-asset/20-mutation rights verifier. The focused Laravel suite + remained 23/23 tests and 149 assertions. +- Reproduced the iOS project with XcodeGen. A combined unsigned test-host trial correctly + failed only the Keychain canary with `errSecMissingEntitlement` (`-34018`), while its UI + target passed 6/6. Rejected that aggregate run and separated signing concerns. +- The accepted iPhone 16e/iOS 26.3.1 runs passed 11/11 unit tests and 6/6 UI journeys in + clean result bundles with normal simulator ad-hoc signing. The separately unsigned + Release application passed the exact identity, privacy, dependency, no-network, + no-deep-link, no-test-hook, English-copy, and seven-background boundary. +- Preserved the other process's API 35 emulator on port 5554. Cold-booted a second + read-only `hb` instance on port 5556 with snapshots disabled. Forced 108/108 Gradle + tasks, passing 8/8 Debug and 8/8 Release JVM tests, both lint variants, both assemblies, + the unsigned Release APK boundary, and 10/10 installed journeys with zero failures, + errors, or skips. Terminated only port 5556. + +### Checklist and safety accounting + +- Added immediate-sequence milestone 23 for the completed evidence/control slice. The + parent source-archive/license/attribution checkbox remains open because 0/7 assets have + the complete durable original/source/terms archive and named release-owner decision. +- Exact parent checklist accounting remains 42 checked, 12 open, 54 total: + **77.78% complete**. +- This slice made no production database access or sample-patient change. The authorized + Nightingale reference patient remains pending/inactive and unreachable. No runtime + route, source adapter, identity provider, patient content, notification, signing, + distribution, deployment, pilot, or activation was added. diff --git a/docs/evidence/nightingale/accessibility-language-readiness-2026-07-27/README.md b/docs/evidence/nightingale/accessibility-language-readiness-2026-07-27/README.md new file mode 100644 index 00000000..e5e18e0c --- /dev/null +++ b/docs/evidence/nightingale/accessibility-language-readiness-2026-07-27/README.md @@ -0,0 +1,23 @@ +# Nightingale accessibility and language-readiness evidence — 2026-07-27 + +**Evidence class:** non-PHI local engineering evidence. This is not accessibility +conformance, translation approval, clinical review, signed distribution, or live-use +authorization. + +The decision record, methods, test results, hashes, corrected failures, and explicit open +gates are in the +[foundation accessibility and language-readiness evidence](../../../nightingale/FOUNDATION-ACCESSIBILITY-LANGUAGE-READINESS-2026-07-27.md). + +## Retained files + +| File | Purpose | SHA-256 | +| ------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------ | +| [ios-debug-double-length.png](./screenshots/ios-debug-double-length.png) | iOS Debug rendered double-length copy and reflow | `05023f1954ec978e478bd79c6a9d5c0a1c0dc7fa8cd2838de7994011d6dafbdd` | +| [ios-debug-rtl.png](./screenshots/ios-debug-rtl.png) | iOS Debug right-to-left layout with governed English fallback | `e7c0c62341732910930f5030587a2a2f7f7e99158764c15f0e2edd5b91387ba7` | +| [android-debug-ar-xb-secure.png](./screenshots/android-debug-ar-xb-secure.png) | Android secure-capture result; black because `FLAG_SECURE` remains active | `c35bacdb98b522206335afa5b9baffd2e4e3352a40749bb747e469cd403af514` | +| [android-debug-en-xa.xml](./hierarchies/android-debug-en-xa.xml) | Android Debug accessibility hierarchy with expanded `en-XA` copy | `3bbf3178a99659de488df1d7184cfb3e552ca418159ff5d8cd0301a2e770794a` | +| [android-debug-ar-xb.xml](./hierarchies/android-debug-ar-xb.xml) | Android Debug accessibility hierarchy with bidirectional `ar-XB` copy and mirrored content bounds | `720d2c8d9b31e3f850dbee46def8e1e139f797a30b242e396d58d8bc749f22fc` | + +All states contain only the offline foundation's nonclinical source copy. The files +contain no patient, principal, encounter, care-team, credential, token, or production +source data. diff --git a/docs/evidence/nightingale/accessibility-language-readiness-2026-07-27/hierarchies/android-debug-ar-xb.xml b/docs/evidence/nightingale/accessibility-language-readiness-2026-07-27/hierarchies/android-debug-ar-xb.xml new file mode 100644 index 00000000..4628ae1d --- /dev/null +++ b/docs/evidence/nightingale/accessibility-language-readiness-2026-07-27/hierarchies/android-debug-ar-xb.xml @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/docs/evidence/nightingale/accessibility-language-readiness-2026-07-27/hierarchies/android-debug-en-xa.xml b/docs/evidence/nightingale/accessibility-language-readiness-2026-07-27/hierarchies/android-debug-en-xa.xml new file mode 100644 index 00000000..87f0ed07 --- /dev/null +++ b/docs/evidence/nightingale/accessibility-language-readiness-2026-07-27/hierarchies/android-debug-en-xa.xml @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/docs/evidence/nightingale/accessibility-language-readiness-2026-07-27/screenshots/android-debug-ar-xb-secure.png b/docs/evidence/nightingale/accessibility-language-readiness-2026-07-27/screenshots/android-debug-ar-xb-secure.png new file mode 100644 index 00000000..93a20397 Binary files /dev/null and b/docs/evidence/nightingale/accessibility-language-readiness-2026-07-27/screenshots/android-debug-ar-xb-secure.png differ diff --git a/docs/evidence/nightingale/accessibility-language-readiness-2026-07-27/screenshots/ios-debug-double-length.png b/docs/evidence/nightingale/accessibility-language-readiness-2026-07-27/screenshots/ios-debug-double-length.png new file mode 100644 index 00000000..5484f9cd Binary files /dev/null and b/docs/evidence/nightingale/accessibility-language-readiness-2026-07-27/screenshots/ios-debug-double-length.png differ diff --git a/docs/evidence/nightingale/accessibility-language-readiness-2026-07-27/screenshots/ios-debug-rtl.png b/docs/evidence/nightingale/accessibility-language-readiness-2026-07-27/screenshots/ios-debug-rtl.png new file mode 100644 index 00000000..934961c7 Binary files /dev/null and b/docs/evidence/nightingale/accessibility-language-readiness-2026-07-27/screenshots/ios-debug-rtl.png differ diff --git a/docs/evidence/nightingale/background-rights-review-2026-07-27/README.md b/docs/evidence/nightingale/background-rights-review-2026-07-27/README.md new file mode 100644 index 00000000..fa1beb23 --- /dev/null +++ b/docs/evidence/nightingale/background-rights-review-2026-07-27/README.md @@ -0,0 +1,201 @@ +# Nightingale background rights review acceptance evidence + +**Date:** 2026-07-27 + +**Scope:** Hash-bound rights/source-archive review, fail-closed distribution hold, complete +Nightingale regression acceptance + +**Functional checklist credit:** Immediate implementation milestone 23 only + +**Parent checklist change:** None; remains 42/54 complete (77.78%) + +## 1. Evidence boundary + +This slice adds no image-rights approval. It establishes an auditable queue and prevents +the existing v0 record from claiming approval. + +Accepted facts: + +- seven review rows match the seven catalog source IDs, filenames, dimensions, and + SHA-256 values; +- two exact Unsplash asset pages are identified; +- one current provider download matches its catalog source byte-for-byte; +- one provider asset is corroborated as the same resized/re-encoded image; +- five exact sources remain unresolved; +- zero durable source archives are recorded; +- zero durable terms snapshots are recorded; +- zero release-owner approvals are recorded; and +- zero assets are distribution eligible. + +Not claimed: + +- legal clearance; +- complete license or attribution evidence; +- a durable organization-controlled original archive; +- patient-advisor or accessibility approval; +- distribution signing or store readiness; +- pilot, production, marketing, deployment, or activation permission. + +## 2. Provider-source observations + +The two filename-embedded provider asset IDs were resolved to official asset pages: + +| Catalog asset | Official provider page | Creator | Provider dimensions | Current download relation | +| ------------- | ------------------------------------------------------------------------------------------------------------- | ---------------- | ------------------- | ----------------------------------------------------------------------------------------------- | +| 05 | [Unsplash `VCq1vozVMbw`](https://unsplash.com/photos/a-small-bird-perched-on-a-tree-branch-VCq1vozVMbw) | Miguel Alcântara | 2608x3912 | supplied 2400x3600 source is the same image after resize/re-encoding; not a byte-for-byte match | +| 06 | [Unsplash `g3mOCylYkmk`](https://unsplash.com/photos/a-small-bird-sitting-on-top-of-a-brick-wall-g3mOCylYkmk) | Muhammad Shakir | 3456x5184 | current download SHA-256 exactly equals catalog source SHA-256 `507f7325…37f1` | + +The pages and the [current provider license](https://unsplash.com/license) were observed +on 2026-07-27. The [provider terms](https://unsplash.com/terms) were inspected for the +license boundary. No full webpage was copied into the repository, and the live +observations are deliberately marked non-durable and unapproved. + +No exact official source was established for catalog assets 01, 02, 03, 04, or 07. Search +results, filename wording, visually similar images, and photographer profiles were +rejected as insufficient evidence. + +## 3. Mechanical acceptance + +Commands: + +```bash +node scripts/ci/verify-nightingale-background-assets.mjs . --self-test +node scripts/ci/verify-nightingale-background-rights.mjs . --self-test +bash scripts/ci/verify-nightingale-product-boundary.sh . +``` + +Accepted output: + +```text +Nightingale background catalog verified: 7 exact metadata-stripped decorative JPEGs, +4439974 bytes, stable daily selection policy, accessibility safeguards, source lineage, +and explicit pre-distribution rights gate; fail-closed self-tests passed. + +Nightingale background rights review verified: 7 catalog-bound assets, 2 provider pages +identified, 1 current provider binary match, 5 exact sources unresolved, +0 distribution-eligible assets; release remains on hold; 20 negative mutations passed. + +Nightingale native product boundary verified +``` + +The 20 negative mutations covered: + +1. distribution approval; +2. legal determination; +3. release-owner approval; +4. automated release; +5. missing asset; +6. duplicate asset; +7. source-hash drift; +8. per-asset release eligibility; +9. machine-local path promoted to archive; +10. invented archive checksum verification; +11. guessed provider for an unresolved source; +12. provider-asset-ID drift; +13. resized corroboration promoted to a binary match; +14. exact-download hash drift; +15. invented terms-snapshot location; +16. invented terms review; +17. rights-cleared count inflation; +18. distribution-eligible count inflation; +19. filename promoted to rights evidence; and +20. catalog-digest drift. + +## 4. Full contract and backend acceptance + +The complete 15-stage chain passed: + +1. empty/default-off executable contract; +2. encounter-access candidate; +3. Today candidate; +4. patient-journey reference candidate; +5. identity/source candidates; +6. 65-source identity/input classification; +7. 130-source communication/notification classification; +8. 134-source journey/preference/release classification; +9. threat/hazard model; +10. dependency inventory; +11. namespace foundation; +12. controlled-pilot manifest; +13. background-rights review; +14. route-free/default-deny backend foundation; and +15. native product boundary. + +The focused Laravel foundation suite passed: + +```text +Tests: 23 passed (149 assertions) +``` + +## 5. iOS Simulator and Release artifact + +The generated project was reproduced with XcodeGen. An isolated iPhone 16e simulator on +iOS 26.3.1 produced: + +- Nightingale unit tests: 11/11 passed; +- Nightingale UI journeys: 6/6 passed; +- failures: 0; +- skipped: 0; and +- unsigned Release Simulator application boundary: passed. + +The first combined invocation deliberately set `CODE_SIGNING_ALLOWED=NO` for all test +hosts. Its UI target passed 6/6, while the Keychain canary received +`errSecMissingEntitlement` (`-34018`) because the unsigned test host lacked its simulated +keychain entitlement. That aggregate invocation was rejected. The accepted rerun +separated the concerns: + +- unit tests used normal simulator ad-hoc signing and passed 11/11; +- UI tests used normal simulator ad-hoc signing and passed 6/6 in a clean result bundle; + and +- the Release artifact was built separately with signing disabled and passed the exact + identity, version, orientation, privacy, dependency, no-network, no-deep-link, + no-test-hook, English-copy, and seven-background verifier. + +Only the Nightingale simulator used for the run was shut down. + +## 6. Android API 35 and Release artifact + +The existing API 35 emulator on port 5554 belonged to another process and remained +untouched. A second `hb` instance was cold-booted read-only on port 5556 with snapshots +disabled. + +Accepted results: + +- forced Debug JVM tests: 8/8 passed; +- forced Release JVM tests: 8/8 passed; +- Debug lint: passed; +- Release lint: passed; +- Debug assembly: passed; +- Release assembly: passed; +- forced build tasks: 108/108 executed; +- unsigned Release APK boundary: passed; +- installed API 35 journeys: 10/10 passed; +- installed failures/errors/skips: 0/0/0. + +The Release verifier confirmed exact package identity, one package-local permission, no +network/deep-link/test hook, cleartext denial, exact English copy without Debug +pseudolocales, expected native libraries, unsigned state, and the exact seven background +JPEGs. + +Only emulator 5556 was terminated. Emulator 5554 remained connected to its original +process. + +## 7. Product and production boundary + +The master checklist remains: + +- checked: 42; +- open: 12; +- total: 54; +- count-based completion: 77.78%. + +Immediate milestone 23 records completion of the evidence/control slice but does not add a +parent checkbox. The parent source-archive/license/attribution item remains open because +0/7 assets have the complete durable evidence and named release-owner decision. + +This work made no production database query or mutation. The already-authorized +Nightingale reference patient remains pending/inactive and isolated exactly as recorded +in the +[sample-patient evidence](../production-sample-patient-2026-07-27/README.md). No +credential, route, source adapter, patient disclosure, clinical content, signing, +distribution, deployment, pilot, or activation was added. diff --git a/docs/evidence/nightingale/brand-identity-2026-07-26/CROSS-SURFACE-AUDIT.md b/docs/evidence/nightingale/brand-identity-2026-07-26/CROSS-SURFACE-AUDIT.md new file mode 100644 index 00000000..6c0b7271 --- /dev/null +++ b/docs/evidence/nightingale/brand-identity-2026-07-26/CROSS-SURFACE-AUDIT.md @@ -0,0 +1,426 @@ +# Hummingbird and Nightingale Cross-Surface Brand and Upgrade Audit + +**Audit date:** 2026-07-26 + +**Evidence class:** Non-PHI repository, build-artifact, and emulator engineering evidence + +**Products:** Hummingbird Staff and Nightingale + +**Predecessor source:** `84b5f8305a694128423ae489fa4527e4b542927f` + +**Current source under test:** `06441141edd38ffd79803a5aed8b8906f6a32a20` plus +the uncommitted verifier/documentation slice described in this record + +**Disposition:** Repository-level brand isolation and source-predecessor install +compatibility pass. Distribution, released-artifact upgrade, store-console, signing, and +future notification-rendering approval remain held. + +## 1. Question and conclusion + +This audit answers five narrow engineering questions: + +1. Do the active Hummingbird and Nightingale applications retain independent, correctly + named application identities? +2. Can Hummingbird's existing notification, widget, and Live Activity surfaces accidentally + acquire the Nightingale mark or namespace from the repository? +3. Does Nightingale currently expose any unapproved notification, widget, extension, shared + container, shortcut, or push surface? +4. Does the repository contain a store-listing package or screenshot asset that could be + mistaken for approved distribution material? +5. Can the current Hummingbird build replace a clean build from the current `origin/main` + predecessor without changing app identity or losing synthetic private app data? + +The repository-level answer is **yes, the products are mechanically isolated**, and the +source-predecessor Hummingbird replacement test passes on iOS Simulator and Android API 35. +The answer is deliberately **not** “release upgrade approved”: + +- the predecessor is a build from repository source, not a retained App Store or Play Store + binary; +- simulator/debug signing is not distribution signing; +- predecessor and current artifacts both use development version/build values rather than a + store-valid increment; +- no Apple or Google console record was inspected or changed; +- no distribution-rights, product-design, accessibility, privacy, or release approval was + inferred; and +- Hummingbird Android does not yet post notifications, while Nightingale has no approved + push or notification implementation to render. + +## 2. Audit boundary and method + +### 2.1 Included + +- application IDs, bundle IDs, display names, extension IDs, app-group names, and launcher + resource references; +- iOS APNs entitlement namespaces, push-routing source, WidgetKit configuration, and Live + Activity ownership; +- Android widget receiver configuration and notification-channel registration; +- positive inventory of Hummingbird notification/widget surfaces; +- negative inventory of Nightingale notification/widget/push/shortcut surfaces; +- repository-owned store-listing directories and store screenshot assets below the four + native product roots; +- cross-product icon, in-app brand-mark, adaptive, round, and monochrome asset fingerprints; +- clean in-place installation of predecessor then current Hummingbird artifacts on an iPhone + 17 Pro Simulator and the `hb` Android API 35 emulator; and +- preservation of a clearly synthetic canary written to each application's private data + area before replacement. + +### 2.2 Excluded + +- production or patient data; +- Apple Developer, App Store Connect, Google Play Console, Firebase, APNs provider, or + distribution-signing access; +- a previously distributed Hummingbird IPA, App Store receipt, AAB, or Play-delivered APK; +- push delivery, notification-center rendering, notification permission journeys, and + notification attachment rendering; +- widget gallery screenshots, widget upgrade persistence, and Live Activity push updates; +- store screenshots, metadata, localization, ratings, privacy declarations, support URLs, + artwork rights, and release notes; and +- Nightingale notification, widget, shared-container, shortcut, or deep-link design, because + none is approved or implemented. + +### 2.3 Evidence controls + +- The predecessor worktree was detached at the exact source SHA above. +- Both predecessor and current Hummingbird apps were built locally from their own checked-out + source. +- Each emulator was cleaned of the Hummingbird package before predecessor installation. +- The canary contained only a product label and source-SHA fragment; it was not derived from + a person, encounter, credential, token, or production system. +- Current artifacts were installed over the predecessor without uninstalling between builds. +- The test then read the canary through the application-private storage boundary. +- Repository scans ignore generated Android `build/` and `.gradle/` trees so compiled + duplicates cannot mask source ownership. +- No production database, route, feature flag, migration, deployment, or store record was + accessed or changed. + +## 3. Canonical identity results + +| Surface | Hummingbird Staff | Nightingale | Result | +| ---------------------------- | ------------------------------------------------- | -------------------------- | -------------------------------- | +| Apple application bundle | `net.acumenus.hummingbird` | `net.acumenus.nightingale` | Distinct | +| Android application ID | `net.acumenus.hummingbird` | `net.acumenus.nightingale` | Distinct | +| Display name | `Hummingbird` | `Nightingale` | Distinct | +| Apple widget extension | `net.acumenus.hummingbird.HummingbirdWidgets` | None | Staff-only | +| Apple app group | `group.net.acumenus.hummingbird` | None | Staff-only | +| APNs entitlement | Hummingbird development/production entitlements | None | Staff-only | +| Android widget receiver | `.widget.HouseGlanceReceiver` | None | Staff-only | +| Android notification surface | Four registered Hummingbird urgency channels only | None | No rendered notification claimed | +| Legacy patient-reference ID | `net.acumenus.hummingbird.patient` | Not reused | Preserved as lineage only | +| Runtime API boundary | Staff `/api/mobile/v1` | None in foundation app | Independent | + +The Hummingbird identity files are byte-for-byte unchanged between the source predecessor +and the current branch for the compared project, manifest, and build configuration paths. +The Hummingbird artwork change therefore remains an update to the existing engineering +application identity; it does not create Nightingale, a patient mode, or a second staff +listing. + +## 4. Hummingbird iOS notification audit + +### 4.1 Implemented surface + +`Hummingbird/Networking/PushManager.swift` imports `UserNotifications`, requests alert, +badge, and sound authorization, registers with APNs when authorized, accepts an APNs device +token, presents foreground banners, and maps a tapped notification's `tab` value to a staff +tab. The source states that notification payloads are generic and PHI-free, but this brand +audit does not independently prove server payload contents. + +The Hummingbird app declares: + +- sandbox APNs entitlement value `development` for Debug; +- production APNs entitlement value `production` for Release; +- background mode `remote-notification`; and +- the staff app group `group.net.acumenus.hummingbird` in both app entitlement files. + +### 4.2 Brand behavior + +There is no `UNNotificationServiceExtension`, `UNNotificationContentExtension`, or +`UNNotificationAttachment` source in the Hummingbird iOS root. The repository therefore +does not own a custom notification image or alternate notification-content UI. System +notification presentation inherits the installed Hummingbird application identity and its +AppIcon. + +Mechanical controls reject: + +- `Nightingale` or `net.acumenus.nightingale` in the Hummingbird project, application + plist, widget root, Android build identity, or Android manifest; +- `nightingale` in Hummingbird entitlement and push-manager sources; and +- a custom notification extension or attachment entering the audited Hummingbird iOS root + without an explicit audit update. + +### 4.3 Residual holds + +- No APNs delivery was triggered. +- No notification-center or lock-screen screenshot was captured. +- No payload, category, action, interruption level, localization, redaction, or deep-link + allowlist was approved. +- The installed AppIcon is mechanically and visually verified elsewhere in this evidence + package, but distribution-signing and released-binary behavior remain unverified. + +## 5. Hummingbird Android notification audit + +### 5.1 Implemented surface + +`UrgencyChannels.kt` creates four staff urgency channels: + +| Tier | Channel ID | Importance | Sound | +| ---- | --------------- | ---------- | -------- | +| T1 | `hb_urgency_t1` | High | Enabled | +| T2 | `hb_urgency_t2` | Default | Disabled | +| T3 | `hb_urgency_t3` | Low | Disabled | +| T4 | `hb_urgency_t4` | Minimum | Disabled | + +The class explicitly describes this as registration only. The active Android source has no +`FirebaseMessagingService`, `NotificationCompat`, `setSmallIcon`, or +`POST_NOTIFICATIONS` declaration. Accordingly, there is no actual Android notification +small-icon or rendered-notification surface to approve. + +### 5.2 Brand behavior and hold + +The channel implementation remains under `net.acumenus.hummingbird.notifications`. The +cross-surface verifier rejects Nightingale vocabulary in the active staff identities and +rejects the appearance of an unreviewed notification-posting implementation. + +This is a **bounded pass**, not notification parity: if notification posting, FCM, a small +icon, runtime permission handling, or custom content is introduced, this audit must reopen +before release. + +## 6. Hummingbird widget and Live Activity audit + +### 6.1 Apple extension inventory + +The `HummingbirdWidgets` extension owns: + +- `HouseGlanceWidget`; +- `ForYouWidget`; +- `JobLiveActivity`; +- the `HummingbirdWidgetsBundle`; +- Hummingbird-only extension entitlements; and +- shared, staff-scoped Live Activity and glance-cache contracts. + +Its exact extension bundle ID is +`net.acumenus.hummingbird.HummingbirdWidgets`, its display name is `Hummingbird`, its +extension point is `com.apple.widgetkit-extension`, and its app group is +`group.net.acumenus.hummingbird`. + +The widget source contains no Nightingale string or namespace. It contains no PNG, JPEG, +PDF, or SVG artwork. Its empty state says “Open Hummingbird to sync.” The repository +therefore cannot place the supplied Nightingale mark in the widget from an extension-owned +asset. Widget rendering remains a staff data-glance experience and does not become a +patient-facing surface. + +### 6.2 Android widget inventory + +The active manifest declares the non-exported +`.widget.HouseGlanceReceiver` for `APPWIDGET_UPDATE`. Its source namespace is +`net.acumenus.hummingbird.widget`, and its provider metadata uses the platform loading +layout rather than a product raster preview image. The receiver and provider metadata +contain no Nightingale reference. + +### 6.3 Residual holds + +- Widget gallery/picker presentation was not captured in this slice. +- An already-installed widget was not migrated across a distributed app upgrade. +- Live Activity state migration and push-to-start behavior were not exercised. +- Product-design and accessibility review of widget content remain separate. +- Adding any explicit widget artwork, preview image, or Nightingale widget reopens this + audit. + +## 7. Nightingale negative-surface audit + +The Nightingale foundation applications intentionally have: + +- no iOS WidgetKit or ActivityKit import; +- no WidgetBundle, app extension, extension plist, app group, or shared container; +- no iOS UserNotifications import, APNs registration, background remote-notification mode, + APNs entitlement, notification service, notification content extension, or attachment; +- no Android AppWidget/Glance receiver or shortcut; +- no Android Firebase Messaging service, notification channel, `NotificationCompat`, + runtime notification permission, or notification-posting code; and +- no API or native network client. + +The verifier fails closed if any of these surface classes enter Nightingale application +source before the governed audit is updated. Absence is the approved foundation behavior; +it is not a promise that Nightingale will never have these capabilities. + +## 8. Store-listing and screenshot audit + +No `fastlane`, `store-listing`, `store_metadata`, or `store-metadata` directory exists +below the Hummingbird or Nightingale iOS/Android product roots. No file matching the audited +App Store/Play Store screenshot naming patterns exists there. + +The launcher and splash captures in this evidence directory are engineering evidence, not +store assets. They must not be copied into a listing without the separate checks in the +product identity and support naming checklist. + +This negative repository result does not establish: + +- that an external Apple or Google listing does not exist; +- that an external listing has the correct icon, name, screenshots, or audience; +- ownership of either supplied mark; +- approved localization, support, privacy, rating, or marketing metadata; or +- authorization to create or modify a store record. + +## 9. Cross-product generated-asset controls + +`scripts/ci/verify-mobile-brand-assets.sh` now checks: + +- the pinned SHA-256 of each supplied source image; +- iOS 1024 px dimensions and opaque RGB encoding; +- Android density-specific launcher, round, adaptive-foreground, and monochrome dimensions; +- transparency policy for adaptive and monochrome resources; +- white-only monochrome silhouette pixels; +- Android 13+ monochrome resource wiring; +- non-identity of the two iOS AppIcon masters; +- non-identity of the two in-app brand marks; and +- non-identity of every Hummingbird/Nightingale Android launcher, round, foreground, and + monochrome resource at every audited density. + +`scripts/ci/verify-mobile-brand-surfaces.sh` adds identity, extension, notification, widget, +shortcut, store-package, and cross-product-name checks. Both scripts run in the independent +macOS mobile-brand CI job. + +These controls are source-level drift detectors. They do not replace human inspection of a +compiled binary or external store surface. + +The brand scripts passed locally after their CI wiring. The final regression also passed +all three Nightingale contract/candidate verifiers with their negative self-tests, the +dependency-free backend verifier with its negative self-tests, the native no-network +product-boundary verifier, targeted formatting, relative-link validation, Git whitespace +validation, and the changed-slice production-connection-token scan. + +## 10. Source-predecessor installed-upgrade evidence + +### 10.1 Common protocol + +1. Build predecessor Hummingbird from detached source + `84b5f8305a694128423ae489fa4527e4b542927f`. +2. Build current Hummingbird from + `06441141edd38ffd79803a5aed8b8906f6a32a20`. +3. Remove any prior Hummingbird install from the engineering emulator. +4. Install the predecessor artifact. +5. Write `hummingbird-upgrade-canary-84b5f830` into application-private storage. +6. Install the current artifact over the predecessor without uninstalling. +7. Read and compare the canary. +8. Verify application and extension identities and, on Android, signer equality. +9. Uninstall the test application and return the iOS simulator to Shutdown. The Android + emulator may remain booted only for the subsequent native regression suite. + +### 10.2 iOS Simulator result + +| Assertion | Predecessor | Current | Result | +| ------------------- | --------------------------------------------- | -------------------------- | --------------------------------------- | +| App bundle ID | `net.acumenus.hummingbird` | `net.acumenus.hummingbird` | Pass | +| Widget bundle ID | `net.acumenus.hummingbird.HummingbirdWidgets` | Same | Pass | +| Display name | `Hummingbird` | `Hummingbird` | Pass | +| Built short version | `1.0` | `1.0` | Equal, release increment still required | +| Built number | `1` | `1` | Equal, release increment still required | +| Private-data canary | Written before replacement | Read after replacement | Pass | + +The iOS simulator reassigned the raw data-container filesystem path while preserving its +contents. Equality of a simulator-internal path is not an application contract; survival of +the private canary under the same bundle identity is the relevant continuity result. + +Both built `Info.plist` files had SHA-256 +`922b1c73aaaa76dc3b956dfac146978f1e38bc76421445b3a9dc20f3aa4efd03`. +The result proves no identity/plist drift in these local artifacts. It does not prove +distribution-signing continuity, App Store receipt continuity, Keychain migration, widget +installation persistence, or compatibility with a previously released IPA. + +### 10.3 Android API 35 result + +| Assertion | Predecessor | Current | Result | +| ------------------- | ------------------------------------------------------------------ | -------------------------- | ------------------------------------------- | +| Package ID | `net.acumenus.hummingbird` | `net.acumenus.hummingbird` | Pass | +| Version name | `0.1.0` | `0.1.0` | Equal, release increment still required | +| Version code | `1` | `1` | Equal, Play upload increment still required | +| V2 signer SHA-256 | `663f448870c7cf490608d8816a6d0d93cc5f441c0ff8190f53f02155f19df281` | Same | Pass | +| Private-data canary | Written before `install -r` | Read after `install -r` | Pass | + +Artifact SHA-256 values were: + +- predecessor APK: + `0b04a4b30d74242c69691e6534126ad02b59b9c9f8ae142e7db170729bda71e5`; +- current APK: + `4fa3ef48278f209674261a953e664ac68ab9633f10aa6292a5c541e00eabd61a`. + +Different APK hashes are expected because the icon-bearing current artifact differs from +the predecessor. Matching package ID and development signer allowed the replacement, and +the private canary survived. The signer is an Android debug certificate and cannot be used +as evidence of Play App Signing or upload-key continuity. + +### 10.4 Upgrade evidence classification + +| Claim | Status | Reason | +| ---------------------------------------------------------------------------- | ------------ | ---------------------------------------------------- | +| Current source can replace predecessor source on clean engineering emulators | Verified | In-place install and canary checks passed | +| Hummingbird application identity remained stable | Verified | Exact bundle/package IDs match | +| Hummingbird iOS widget extension identity remained stable | Verified | Exact extension ID matches | +| Android debug signer remained stable on this workstation | Verified | Exact certificate digest matches | +| Released user data will migrate correctly | Not verified | No released binary or production schema sampled | +| Keychain/Keystore state will migrate correctly | Not verified | Canary used ordinary app-private files only | +| Existing widget/Live Activity installation survives | Not verified | No installed extension state migrated | +| App Store/Play Store accepts the new artifact | Not verified | Version/build not incremented; consoles not accessed | +| Distribution signing is continuous | Not verified | Local simulator/debug signatures only | +| Rollback preserves data | Not verified | Downgrade and store rollback were not exercised | + +## 11. Findings and required follow-up + +### 11.1 Passed engineering controls + +- Hummingbird and Nightingale retain distinct active product identities. +- Hummingbird's widget, app-group, APNs, and Android widget namespaces remain staff-only. +- Nightingale remains widget-, extension-, shortcut-, notification-, push-, and + network-free. +- No repository-owned store-listing package exists under the audited native roots. +- Every active generated launcher/brand-mark asset compared across products is distinct. +- The current Hummingbird app replaces the repository predecessor in place on both + emulators while preserving a synthetic private-data canary. + +### 11.2 Open engineering/release findings + +1. Hummingbird iOS builds report short version `1.0` even though the project build setting + records marketing version `0.1.0`. Release engineering must choose and mechanically pin + one canonical version source before archiving. +2. Predecessor/current iOS build numbers and Android version codes are both `1`. A real + update must use approved monotonically increasing values. +3. Android notification channels exist without a posting implementation. No Android + notification icon or visual behavior can be approved until posting exists. +4. No retained released artifact was available for binary-to-binary migration testing. +5. No store console or distribution-signing evidence exists. +6. Widget installation persistence, Keychain/Keystore persistence, deep links, analytics, + crash reporting, and downgrade/rollback remain outside this test. + +### 11.3 Release acceptance evidence still required + +Before closing the external portion of Stream B: + +- obtain artwork ownership and distribution-rights approval for both marks; +- identify retained, verifiably released Hummingbird iOS and Android predecessor artifacts; +- verify distribution application IDs, entitlements, signing chains, and store records; +- set and verify monotonic version/build values; +- run released-artifact-to-release-candidate upgrade tests for app-private data, + Keychain/Keystore state, installed widgets, Live Activities, deep links, and any approved + notification state; +- capture Hummingbird notification and widget surfaces only after the relevant capability + exists and uses reviewed generic content; +- create and review store screenshots/metadata with no patient information, internal host, + test hook, stale name, or cross-product asset; +- independently review product design and accessibility; +- attach the evidence to the product identity and support naming checklist; and +- obtain exact-SHA CI and protected-main release approval before any deployment workflow. + +## 12. Reproduction commands + +The stable source checks are: + +```bash +bash -n scripts/ci/verify-mobile-brand-assets.sh +bash -n scripts/ci/verify-mobile-brand-surfaces.sh +bash scripts/ci/verify-mobile-brand-assets.sh . +bash scripts/ci/verify-mobile-brand-surfaces.sh . +``` + +The installed-upgrade protocol intentionally remains a manual engineering procedure until +the team provides retained released artifacts and approved distribution credentials. +Automating only the local-debug predecessor would risk making a source-level result look +like store-release evidence. diff --git a/docs/evidence/nightingale/brand-identity-2026-07-26/README.md b/docs/evidence/nightingale/brand-identity-2026-07-26/README.md new file mode 100644 index 00000000..3964fbd3 --- /dev/null +++ b/docs/evidence/nightingale/brand-identity-2026-07-26/README.md @@ -0,0 +1,68 @@ +# Hummingbird and Nightingale Brand Identity Evidence — 2026-07-26 + +**Evidence class:** Non-PHI engineering visual review. This is not distribution-rights, +App Store, Play Store, accessibility, privacy, clinical, or pilot approval. + +## Scope + +This record verifies the supplied Hummingbird and Nightingale product marks on: + +- iOS 26.3 Simulator launcher in light and dark appearance; +- Android API 35 circular adaptive launcher rendering; +- Android API 35 themed icons in light and dark appearance; and +- Android 12+ system splash surfaces for both applications. + +The isolated iOS review device contained only the two reviewed application products. +Android screenshots came from the repository’s `hb` API 35 engineering emulator and +contain no patient data. + +## Findings and correction + +The first Android round-mask review exposed clipping of the Hummingbird beak at the +original 8% adaptive inset. Engineering increased only the Hummingbird adaptive and +monochrome inset to 20%, rebuilt both Debug and Release, reinstalled the APK, and repeated +the review. The retained evidence shows the corrected full silhouette. + +Both products now render distinct marks, readable silhouettes, complete round-mask +subjects, and stable themed-icon contrast. Nightingale’s smaller natural subject and +source negative space remain legible at the 8% inset. The iOS icons remain full-color in +both launcher appearances by design; no unreviewed alternate dark/tinted artwork is +claimed. + +The icon generator was also corrected so every `opaque` output is an RGB PNG with no +alpha channel. `scripts/ci/verify-mobile-brand-assets.sh` now fails if an iOS or legacy +Android launcher output regains alpha, if themed icons are not white alpha silhouettes, +if dimensions drift, if source checksums change, or if the two iOS masters become +identical. It also fails if the products' in-app brand marks or any corresponding Android +launcher, round, adaptive-foreground, or monochrome density asset becomes identical. + +The companion +[cross-surface brand and upgrade audit](./CROSS-SURFACE-AUDIT.md) inventories notification, +widget, Live Activity, extension, app-group, shortcut, and store-package surfaces. It also +records clean source-predecessor installed-upgrade tests on iPhone 17 Pro Simulator and +Android API 35. Those tests preserve a synthetic private-data canary and exact application +identities, but they are not released-artifact or distribution-signing evidence. + +## Screenshots + +| File | Surface | SHA-256 | +| ------------------------------------------------------------------------------ | ------------------------------------------ | ------------------------------------------------------------------ | +| [ios-launcher-light.png](./screenshots/ios-launcher-light.png) | iOS launcher, light appearance | `9d5f80e76e0d283588c490c317a38e9aeeb0cf70da30fce6acee9861c6965035` | +| [ios-launcher-dark.png](./screenshots/ios-launcher-dark.png) | iOS launcher, dark appearance | `fe4b5bf1e006a1f9dfbdbd2f712aa42f39bbde565efb38e74e70d62c22be745d` | +| [android-round-adaptive.png](./screenshots/android-round-adaptive.png) | Android app drawer, circular adaptive mask | `c5c9be9a1a4f2cad7d54a1cca52fdd28c223f51c2c31819c820871842d90cb3b` | +| [android-themed-light.png](./screenshots/android-themed-light.png) | Android launcher, themed icons, light | `078b458ababacdae58caf81c6bfc0ebbc4526b021d2ec4da2835fb72c3c4ffd9` | +| [android-themed-dark.png](./screenshots/android-themed-dark.png) | Android launcher, themed icons, dark | `6d4a8c28e7b77510704233707690ead407933a8ff16535758c612531d104aa7c` | +| [android-hummingbird-splash.png](./screenshots/android-hummingbird-splash.png) | Hummingbird Android system splash | `f2d1dc47b4e24e0550d3690d88203ea33a732f497e1ecd40b55a2cd1880e4132` | +| [android-nightingale-splash.png](./screenshots/android-nightingale-splash.png) | Nightingale Android system splash | `f974ca75dcaad5ed5c7f8c1d6f1218fcd92776fd92b5d1034e53dbc617f9ce62` | + +## Open holds + +- Supplied-artwork ownership and distribution rights remain unapproved. +- App Store and Play Store listings, screenshots, metadata, signing, and public support + endpoints remain uncreated or unverified. +- Repository-level notification/widget ownership, absence of an unreviewed store package, + and source-predecessor upgrade compatibility are verified. Released-artifact migration, + external store records, distribution signing, version/build increments, installed-widget + persistence, and actual notification rendering remain separate release tasks. +- Engineering visual inspection does not substitute for independent accessibility or + product-design review. diff --git a/docs/evidence/nightingale/brand-identity-2026-07-26/screenshots/android-hummingbird-splash.png b/docs/evidence/nightingale/brand-identity-2026-07-26/screenshots/android-hummingbird-splash.png new file mode 100644 index 00000000..6701cc45 Binary files /dev/null and b/docs/evidence/nightingale/brand-identity-2026-07-26/screenshots/android-hummingbird-splash.png differ diff --git a/docs/evidence/nightingale/brand-identity-2026-07-26/screenshots/android-nightingale-splash.png b/docs/evidence/nightingale/brand-identity-2026-07-26/screenshots/android-nightingale-splash.png new file mode 100644 index 00000000..c38376ce Binary files /dev/null and b/docs/evidence/nightingale/brand-identity-2026-07-26/screenshots/android-nightingale-splash.png differ diff --git a/docs/evidence/nightingale/brand-identity-2026-07-26/screenshots/android-round-adaptive.png b/docs/evidence/nightingale/brand-identity-2026-07-26/screenshots/android-round-adaptive.png new file mode 100644 index 00000000..6cbbfbdc Binary files /dev/null and b/docs/evidence/nightingale/brand-identity-2026-07-26/screenshots/android-round-adaptive.png differ diff --git a/docs/evidence/nightingale/brand-identity-2026-07-26/screenshots/android-themed-dark.png b/docs/evidence/nightingale/brand-identity-2026-07-26/screenshots/android-themed-dark.png new file mode 100644 index 00000000..36692653 Binary files /dev/null and b/docs/evidence/nightingale/brand-identity-2026-07-26/screenshots/android-themed-dark.png differ diff --git a/docs/evidence/nightingale/brand-identity-2026-07-26/screenshots/android-themed-light.png b/docs/evidence/nightingale/brand-identity-2026-07-26/screenshots/android-themed-light.png new file mode 100644 index 00000000..fd7dd384 Binary files /dev/null and b/docs/evidence/nightingale/brand-identity-2026-07-26/screenshots/android-themed-light.png differ diff --git a/docs/evidence/nightingale/brand-identity-2026-07-26/screenshots/ios-launcher-dark.png b/docs/evidence/nightingale/brand-identity-2026-07-26/screenshots/ios-launcher-dark.png new file mode 100644 index 00000000..aeb643af Binary files /dev/null and b/docs/evidence/nightingale/brand-identity-2026-07-26/screenshots/ios-launcher-dark.png differ diff --git a/docs/evidence/nightingale/brand-identity-2026-07-26/screenshots/ios-launcher-light.png b/docs/evidence/nightingale/brand-identity-2026-07-26/screenshots/ios-launcher-light.png new file mode 100644 index 00000000..071d9f4d Binary files /dev/null and b/docs/evidence/nightingale/brand-identity-2026-07-26/screenshots/ios-launcher-light.png differ diff --git a/docs/evidence/nightingale/controlled-pilot-manifest-2026-07-27/README.md b/docs/evidence/nightingale/controlled-pilot-manifest-2026-07-27/README.md new file mode 100644 index 00000000..d6fd8edf --- /dev/null +++ b/docs/evidence/nightingale/controlled-pilot-manifest-2026-07-27/README.md @@ -0,0 +1,210 @@ +# Nightingale controlled-pilot manifest foundation evidence + +- **Date:** 2026-07-27 +- **Feature branch:** `codex/nightingale-patient-product` +- **Pre-slice verified head:** `b333ebf8a618cac5c3c8a27d43ea666de32579bc` +- **Milestone:** Stream F default-off, audited, expiry-bound pilot configuration +- **Checklist result:** 42/54 complete (77.78%); 12/54 remain open +- **Production database activity:** none +- **Application deployment or activation:** none + +## 1. Scope accepted + +This slice defines a generated, non-runnable controlled-pilot manifest candidate. It +contains: + +- opaque facility, unit, and cohort scope; +- inclusion/exclusion policy release references; +- a technical maximum of 25 active enrollments with automatic enrollment prohibited; +- canonical locale-tag and released interpreter-coverage requirements; +- fail-closed unknown/outage exclusion handling without sensitive-service inference; +- IANA-zone, non-overlapping local weekly support windows; +- released urgent-help guidance for uncovered windows; +- an exact UTC start/expiry window no longer than 168 hours; +- eight prerequisite-record references; +- seven distinct named-approval roles; +- nine append-only audit event types with nine content-free fields; +- a default-engaged kill switch and exact rollback evidence; and +- a positive disposition limited to external go/no-go review. + +The committed template contains no real facility, unit, cohort, language, support, +approval, validity, source-deployment, release, audit-sink, rollback, or pilot value. + +## 2. Artifacts + +| Artifact | Purpose | +| ----------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------- | +| [`candidate.json`](../../../nightingale/pilot/candidates/v0/candidate.json) | Generated policy, default manifest, constraints, ceilings, inventories, and four source hashes | +| [`fixtures.json`](../../../nightingale/pilot/candidates/v0/fixtures.json) | Synthetic complete structure plus 34 deterministic evaluation cases | +| [Decision/evidence record](../../../nightingale/CONTROLLED-PILOT-MANIFEST-FOUNDATION-2026-07-27.md) | Field semantics, non-authorization boundary, future requirements, and residual holds | +| [`build-nightingale-controlled-pilot-manifest.mjs`](../../../../scripts/ci/build-nightingale-controlled-pilot-manifest.mjs) | Deterministic builder | +| [`verify-nightingale-controlled-pilot-manifest.mjs`](../../../../scripts/ci/verify-nightingale-controlled-pilot-manifest.mjs) | Independent builder, shape, semantic, source, fixture, and mutation verifier | + +## 3. Candidate-verifier acceptance + +The independent verifier passed: + +```text +Nightingale controlled-pilot manifest verified: 34 synthetic cases (33 hold, 1 external go/no-go review only), 4 checksum-bound sources, 25 negative self-tests. +``` + +It proved: + +- committed JSON is exact builder output; +- all four source SHA-256 values match current bytes; +- the executable Nightingale contract still has zero paths; +- every executable activation fact remains false; +- backend configuration remains code-owned with `not_enrolled`, no disclosure/mutation, + and no production activation; +- the committed template is empty, revision zero, and inactive; +- the only complete synthetic case remains `runtime_activation_permitted=false`; +- 33 incomplete/adversarial cases hold; +- the complete synthetic case may only reach external go/no-go review; +- all 13 implementation/route/network/source/query/enrollment/disclosure/deployment/ + activation constraints remain false; +- no patient/content/credential key enters the manifest or fixture; +- no production host, credential, or executable Nightingale route enters the artifacts; + and +- all 25 deliberate artifact mutations are rejected by validation itself. + +The negative-test harness was separately inspected and corrected so it counts a mutation +only after the validator throws. A self-test cannot pass by throwing its own +“did not fail” assertion. + +## 4. Full contract and backend acceptance + +The full Nightingale chain passed after CI integration: + +1. empty/default-off executable contract and negative tests; +2. encounter-access candidate; +3. Today candidate with 68 cases and 24 negative mutations; +4. 15-family/27-case journey candidate with 23 negative mutations; +5. 64 identity and 42 source cases; +6. 65-source identity/input ledger; +7. 130-source communication/notification ledger; +8. 134-source journey/preference/release ledger; +9. threat/hazard model; +10. dependency inventory; +11. namespace foundation; +12. controlled-pilot manifest; +13. backend foundation; and +14. native product boundary. + +The focused Laravel foundation suite passed: + +```text +Tests: 23 passed (149 assertions) +``` + +No runtime manifest class, service provider, route, controller, database table, patient +query, native client, or enrollment operation was introduced. + +## 5. iOS Simulator acceptance + +An isolated iPhone 16e simulator on iOS 26.3.1 was booted. The Nightingale Xcode project +first reproduced from `project.yml`. + +Accepted results: + +- unit tests: 11/11 passed; +- UI journeys: 6/6 passed; +- failures: 0; +- skipped: 0; +- Debug build-for-testing: passed; +- unsigned Release Simulator build: passed; and +- Release artifact identity, version, orientation, privacy, dependencies, no-network, + no-deep-link, no-test-hook, English-copy, and seven-background boundary: passed. + +The UI suite covered display-comfort persistence, double-length landscape reflow, +accessibility-size landscape reachability, foundation copy, lifecycle privacy cover, and +right-to-left layout. + +The isolated iPhone 16e was shut down by the test trap. No patient data or production +network was used. + +## 6. Android API 35 emulator acceptance + +The pre-existing `hb` API 35 emulator on port 5554 belonged to another process and was not +targeted. A second read-only instance of the same AVD was started on port 5556 without a +snapshot. + +Accepted build and artifact results: + +- Debug and Release lint: passed; +- Debug and Release assembly: passed; +- unsigned Release APK identity, permission, network/deep-link/test-hook, cleartext, + English-copy, native-library, and seven-background boundary: passed; +- installed API 35 journeys: 10/10 passed; +- installed failures/errors/skips: 0/0/0. + +The initial JVM task invocation restored test outputs from the Gradle cache. Those results +were not accepted as fresh execution. A separate forced `--rerun-tasks` invocation then +executed all test tasks: + +- Debug JVM tests: 8/8 passed; +- Release JVM tests: 8/8 passed; +- failures/errors/skips: 0/0/0; and +- actionable tasks: 45/45 executed. + +Only the port-5556 emulator was terminated. Port 5554 remained connected to its original +process. + +## 7. Documentation and repository acceptance + +The following gates are required before publication: + +- exact checklist reconciliation: 42 checked, 12 open, 54 total; +- Prettier over every changed Markdown, JSON, JavaScript, and workflow file; +- local Markdown-link resolution for every changed document; +- JavaScript syntax checks; +- Bash syntax check; +- Git whitespace validation; +- secret scans; and +- exact-SHA hosted CI. + +Final command results and the exact publication SHA are recorded in the companion devlog +and draft pull request after commit. + +## 8. Current-main reconciliation + +The validated slice was committed as `487d1c796fd9359819d501ccb2d3c1c60928b7d9`. +The branch then merged current `origin/main` at +`996a5336066e0523de63c3e88f7bc0e1243ab192`. That upstream commit changes only +`.github/workflows/ci.yml` to provide the shared frontend build through a thin, +independent `frontend-build` job for the Browser and DAST jobs. + +The automatic merge preserved both the upstream job graph and the Nightingale contract +job's controlled-pilot verifier. No application, contract, fixture, database, native +client, or sample-patient source changed after emulator acceptance. The post-merge +verification therefore reruns the affected workflow parse and Nightingale mechanical +gates; the recorded iOS and Android results continue to apply to the exact unchanged +runtime trees. + +While exact-SHA CI was running, `origin/main` advanced to +`98d9bf98f2e811f6b39c0d14bb5e0ea1b5ea7317` with documentation for the same CI +scheduler change. The branch merged that documentation-only commit. Pinned Gitleaks +8.28 then reported one ordinary prose phrase in the new upstream devlog under its generic +API-key rule. The finding matched the existing Nightingale devlog false-positive class: +`notification/delivery` following the words `API paths`. + +The existing `AND`-scoped exception was extended only to the exact upstream devlog path +and exact captured phrase. A checksum-verified Gitleaks 8.28 binary then scanned all 1,082 +locally visible commits with zero findings. The exception does not match any other path or +value and does not weaken the generic API-key rule for credentials. + +## 9. Explicit non-authorization + +This evidence does not approve: + +- a real pilot manifest or any of its scope values; +- any named reviewer or approval; +- identity proofing, recovery, consent, representatives, or sensitive-service handling; +- a source connector, audit sink, evaluator, enrollment service, support service, or + rollback system; +- clinical content or a patient-visible operation; +- signed distribution, physical-device acceptance, store submission, pilot enrollment, + or deployment; or +- any production database access or mutation. + +The separately authorized Nightingale sample patient remains pending/inactive and +unreachable. This slice did not read or alter it. diff --git a/docs/evidence/nightingale/current-main-reconciliation-2026-07-27/README.md b/docs/evidence/nightingale/current-main-reconciliation-2026-07-27/README.md new file mode 100644 index 00000000..92f120bb --- /dev/null +++ b/docs/evidence/nightingale/current-main-reconciliation-2026-07-27/README.md @@ -0,0 +1,170 @@ +# Nightingale current-main product-boundary reconciliation + +- **Date:** 2026-07-27 +- **Feature branch:** `codex/nightingale-patient-product` +- **Verified pre-merge Nightingale baseline:** `21e4de17e27c258955c0b14ba490f9d0d4be16b5` +- **Merged `origin/main`:** `ea14d2bd2be9656555dfba35da2dbf28e74730e3` +- **Merge commit:** `bcaae3da` +- **Boundary-restoration commit:** `c11d462cb5c62e399139caf0538ed9def1f1d441` +- **Functional checklist effect:** none; remains 41/54 (75.93%) + +## 1. Why reconciliation was required + +Current `main` contained two changes after the last verified Nightingale feature head: + +1. `f63e599476ed59fff443a8b1ac0c7bbc4f2483fc` updated the Flow 4D + patient-journey/conformance plan. It is unrelated to the Nightingale product boundary + and had to be preserved. +2. `ea14d2bd2be9656555dfba35da2dbf28e74730e3` renamed the legacy + `hummingbird/iosPatientApp`, `hummingbird/androidPatientApp`, patient middleware, + backend configuration, contract, and reference provisioners to Nightingale. + +The second change conflicts with the accepted product direction. Nightingale is an +independent product rooted at `nightingale/iosApp` and `nightingale/androidApp`; the +legacy Hummingbird Patient applications remain deprecated, non-production migration +evidence. Renaming the legacy applications in place would create two source roots claiming +the same Nightingale app identifiers and would collapse the migration boundary that the +plan requires. + +## 2. Reconciliation method + +The feature branch merged current `origin/main` so the unrelated Flow 4D change and the +complete upstream graph were preserved. It then forward-reverted only the incompatible +patient-product rename: + +```text +bcaae3da Merge remote-tracking branch 'origin/main' into codex/nightingale-patient-product +c11d462c Revert "Rename the patient product from Hummingbird Patient to Nightingale (#101)" +``` + +Seven merge/revert conflict paths and the independent Nightingale configuration were +resolved against the exact verified tree at `21e4de17`: + +- `.appledeploy.example` +- `.gitleaks.toml` +- `config/hummingbird-patient.php` +- `config/nightingale.php` +- `docs/hummingbird/TESTFLIGHT.md` +- `hummingbird/iosApp/Hummingbird/Assets.xcassets/AppIcon.appiconset/icon-1024.png` +- `hummingbird/iosPatientApp/HummingbirdPatient.xcodeproj/project.pbxproj` +- `hummingbird/iosPatientApp/project.yml` + +This is a forward reconciliation on the feature branch. It does not rewrite published +history, discard unrelated upstream work, alter production, or assert that the upstream +rename never occurred. + +## 3. Exact tree-equivalence proof + +The boundary-restoration tree at `c11d462c`, before this evidence-only follow-up, differs +from the verified Nightingale baseline in exactly one path: + +```text +M docs/plans/FLOW-4D-PATIENT-JOURNEY-AND-CONFORMANCE-PLAN-2026-07-26.md +``` + +That path is byte-identical to the version introduced by `f63e5994`. Therefore: + +- every Nightingale and legacy Hummingbird Patient source/configuration path is restored + to the exact verified `21e4de17` state; +- the unrelated Flow 4D upstream change is retained; +- no unrelated user or upstream work is dropped. + +At `c11d462c`, the branch was 0 commits behind and 36 commits ahead of +`ea14d2bd`, before adding this evidence-only record. + +## 4. Product-identity invariants after reconciliation + +| Boundary | Required state | Verified state | +| ---------------------- | ------------------------------------------ | ---------------------------------------------------------------------------------------------------------------- | +| Nightingale iOS | independent target and bundle identity | `nightingale/iosApp`; `net.acumenus.nightingale` | +| Nightingale Android | independent app and package identity | `nightingale/androidApp`; `net.acumenus.nightingale` | +| Legacy patient iOS | deprecated Hummingbird Patient identity | `hummingbird/iosPatientApp`; `net.acumenus.hummingbird.patient` | +| Legacy patient Android | deprecated Hummingbird Patient identity | `hummingbird/androidPatientApp`; `net.acumenus.hummingbird.patient` | +| Nightingale backend | route-free, code-owned, inert foundation | `/api/nightingale/v1` reserved; all route/network/identity/source/disclosure/mutation/production states disabled | +| Legacy backend | retained compatibility and migration input | `config/hummingbird-patient.php` and legacy contract restored | +| Distribution source | independent Nightingale root only | `.appledeploy.example` points Nightingale to `nightingale/iosApp` | + +Repository scans found: + +- no `net.acumenus.nightingale` runtime identity under either legacy Hummingbird Patient + native root; +- no `net.acumenus.hummingbird.patient` identity under either independent Nightingale + native root; +- no executable Nightingale route, network permission, source adapter, identity provider, + content release, notification provider, or production activation. + +## 5. Reacceptance evidence + +### 5.1 Contract, governance, and backend + +The full Nightingale contract/boundary chain passed: + +- empty/default-off contract and negative self-tests; +- held encounter-access, Today, and patient-journey candidates; +- 64 identity cases and 42 source cases; +- 65-source identity, 130-source communication/notification, and 134-source + journey/preference/release ledgers, covering the exact 256-source predecessor universe; +- threat/hazard model; +- dependency inventory; +- namespace manifest and eight negative mutations; +- backend foundation and native product-boundary verifiers. + +The focused Laravel suite passed 23 tests and 149 assertions. + +### 5.2 iOS Simulator and Release artifact + +The reconciled Xcode project matched `project.yml`. A fresh build-for-testing ran on an +iPhone 16e simulator with iOS 26.3.1: + +- Nightingale unit tests: 11/11 passed; +- Nightingale UI journeys: 6/6 passed; +- failures: 0; +- skipped: 0. + +An unsigned Release simulator application then passed the exact identity, version, +orientation, privacy-manifest, linked-dependency, no-network/no-deep-link/no-test-hook, +English-copy, and seven-background artifact boundary. Only the Nightingale simulator +started for this run was shut down. + +### 5.3 Android unit, build, emulator, and Release artifact + +The clean Android acceptance passed: + +- Debug unit tests: 8/8 passed after forced uncached execution; +- Release unit tests: 8/8 passed after forced uncached execution; +- Debug and Release lint: passed; +- Debug and Release assembly: passed; +- unsigned Release APK boundary: passed. + +The pre-existing API 35 emulator on port 5554 belonged to another process and was left +untouched. A second read-only API 35 instance was started on port 5556. All 10/10 installed +Nightingale journeys passed with zero failures, errors, or skips. Only the port-5556 +instance was terminated afterward; port 5554 remained connected. + +## 6. Sample-patient and production boundary + +This reconciliation performed no production database access or mutation. The already +authorized sample remains exactly as documented in +[the production sample-patient evidence](../production-sample-patient-2026-07-27/README.md): +one pending/inactive, contactless/passwordless Nightingale principal and one synthetic +operational encounter derived from the safe shape of the deprecated Hummingbird Patient +reference. It has no identity link, grant, challenge, session, projection, application +caller, or activation. + +No migration, deployment, signing, upload, distribution, pilot enrollment, clinical +approval, content release, source-connector deployment, or patient disclosure occurred. + +## 7. Checklist accounting and remaining holds + +This work restores and proves an already-accepted architectural boundary. It does not +deliver a new functional capability, approval, live integration, or release control. +Accordingly, no functional checkbox is newly closed: + +- complete: 41/54; +- incomplete: 13/54; +- count-based completion: 75.93%. + +The remaining items still require released operation implementations, named +identity/source/content/reviewer approvals, rights and attribution evidence, signed +distribution proof, human accessibility and patient-advisor review, production-like +integration/failover exercises, and controlled-pilot authorization. diff --git a/docs/evidence/nightingale/production-sample-patient-2026-07-27/README.md b/docs/evidence/nightingale/production-sample-patient-2026-07-27/README.md new file mode 100644 index 00000000..3be70ad9 --- /dev/null +++ b/docs/evidence/nightingale/production-sample-patient-2026-07-27/README.md @@ -0,0 +1,207 @@ +# Nightingale production sample-patient evidence + +**Date:** 2026-07-27 + +**Environment:** `pgsql.acumenus.net`, database `zephyrus` + +**Operator role:** `smudoshi` + +**Authorization:** explicit user direction to create a Nightingale sample patient from the +deprecated Hummingbird Patient reference patient + +**Classification:** synthetic operational evidence; no real patient data or enrollment +material + +**Product activation:** none + +No password, token, encrypted value, keyed digest, challenge hash, application key, patient +contact value, or patient-authored/clinical content is retained in this evidence. + +## 1. Outcome + +Production now contains one Nightingale-owned synthetic sample with two deliberately +separate records: + +| Layer | Stable lookup marker | State | +| --------------------- | -------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------- | +| Operational encounter | `patient_ref = demo-nightingale-reference-inpatient` and `created_by = nightingale-reference-patient-provisioner-v1` | active synthetic encounter, unit 85, no bed, acuity 2, no discharge timestamp, not deleted | +| Patient principal | `preferences.provisioning.owner = nightingale-reference-patient-provisioner-v1` | patient principal, display name `Nightingale Reference Patient`, pending, inactive, no contact or password | + +The principal is intentionally not identity-linked or encounter-authorized. This sample +therefore cannot sign in, receive a session, enumerate a context, read a projection, send a +message, register a notification device, or activate Nightingale. + +## 2. Hummingbird source-template use + +The existing Hummingbird reference patient was used only as a safe synthetic template: + +- one provisioner-owned active operational reference encounter existed; +- one pending/inactive Hummingbird reference principal existed; +- the principal had no email, phone, or password; +- its locale and timezone were `en-US` and `America/New_York`; +- its operational sample used unit 85, no bed, acuity 2, and active/not-discharged state; +- its identity link, grant, challenge material, external UUIDs, keyed digests, encrypted + source references, projection content, and product-owner strings were not copied. + +The Hummingbird source records were preserved unchanged. Post-write reconciliation found +exactly one Hummingbird operational encounter, principal, identity link, and access grant +under their original owner markers. + +## 3. Read-only preflight + +The production session was first forced read-only. Metadata-only checks established: + +| Check | Result | +| ------------------------------------------ | ----------------------------------------------------- | +| Connected database/user | expected database and operator | +| Transaction mode | read-only | +| `prod.encounters` target patient reference | 0 rows | +| Nightingale encounter owner marker | 0 rows | +| Nightingale principal owner marker | 0 rows | +| Nightingale identity-link owner marker | 0 rows | +| Nightingale grant owner marker | 0 rows | +| Selected unit 85 | exists and is not deleted | +| Hummingbird template principal | exactly 1, pending/inactive, contactless/passwordless | +| Insert/sequence privileges | present | +| User-defined `prod.encounters` triggers | none | + +The preflight queried only schema metadata and exact synthetic owner/reference markers. It +did not enumerate real patients. + +## 4. Encounter transaction + +The operational insert used one serializable transaction with: + +- `ON_ERROR_STOP`; +- a 15-second statement timeout; +- a 5-second lock timeout; +- a transaction-scoped advisory lock on the Nightingale provisioner owner; +- exact target-reference and owner-marker zero-cardinality checks; +- an active/non-deleted unit-85 check; +- explicit values for every operational field; and +- a pre-commit exact-row read. + +Any existing target reference, existing owner record, missing/deleted unit, constraint +failure, timeout, or cardinality drift raised an exception and rolled back the complete +transaction. + +The committed encounter has: + +| Field | Value | +| ---------------------------- | ---------------------------------------------- | +| `patient_ref` | `demo-nightingale-reference-inpatient` | +| `unit_id` | 85 (`5 East — Medical/Surgical`) | +| `bed_id` | null | +| `acuity_tier` | 2 | +| `status` | `active` | +| `discharged_at` | null | +| `expected_discharge_date` | 2026-07-29 | +| `created_by` / `modified_by` | `nightingale-reference-patient-provisioner-v1` | +| `is_deleted` | false | + +The expected-discharge date is sample data, not a clinical prediction and not an automatic +retirement mechanism. + +## 5. Principal clone transaction + +The principal insert used a second serializable transaction under the same advisory lock. +It generated a fresh UUIDv7 and selected only safe attributes from the exact Hummingbird +template row: + +- `principal_type`; +- locale; and +- timezone. + +All other values were independently set for Nightingale. The transaction required: + +- exactly one Hummingbird source-template principal; +- exactly one committed Nightingale synthetic encounter; +- no pre-existing Nightingale principal owner marker; +- source principal type `patient`; +- source state pending/inactive; +- null source contact and password values; and +- exact post-insert Nightingale principal cardinality and safety properties. + +The committed principal has: + +| Field | Value | +| -------------------------------------------------- | ------------------------------------------------------- | +| `principal_type` | `patient` | +| `display_name` | `Nightingale Reference Patient` | +| `status` / `is_active` | `pending` / false | +| `email` / `phone_e164` / `password` | null / null / null | +| `preferences.synthetic` | true | +| `preferences.product` | `nightingale` | +| `preferences.provisioning.owner` | `nightingale-reference-patient-provisioner-v1` | +| `preferences.provisioning.mode` | `operator-authorized-production-sample-clone` | +| `preferences.provisioning.source_template_product` | `hummingbird_patient` | +| `preferences.provisioning.source_template_owner` | `hummingbird-patient-reference-identity-provisioner-v1` | +| locale / timezone | `en-US` / `America/New_York` | + +No source identifier, encrypted subject, keyed digest, encounter foreign key, grant, or +credential was added to the principal. + +## 6. Independent post-write reconciliation + +A fresh read-only connection verified this exact result: + +| Invariant | Count/result | +| ------------------------------------------------------------------------------------- | ------------- | +| Nightingale target encounter rows | 1 | +| Rows satisfying every encounter ownership/state constraint | 1 | +| Nightingale owner principals | 1 | +| Principals satisfying every synthetic/product/pending/inactive/contactless constraint | 1 | +| Nightingale principal identity links | 0 | +| Nightingale principal encounter grants | 0 | +| Nightingale principal enrollment challenges | 0 | +| Nightingale principal sessions | 0 | +| Nightingale principal access-audit events | 0 | +| Nightingale principal notification devices | 0 | +| Hummingbird template encounter/principal/identity/grant cardinalities | 1 / 1 / 1 / 1 | + +This proves bounded creation and source preservation. It does not prove that a Nightingale +identity, source, authorization, projection, or native client is ready. + +### 6.1 Direction-confirmation reconciliation + +After the operator reiterated that Nightingale must have a sample patient derived from the +deprecated Hummingbird Patient sample, a new production connection reran the reconciliation +inside a repeatable-read, read-only transaction. It found: + +| Reconfirmed invariant | Count/result | +| --------------------------------------------------------------------------------- | ------------------------------------------------------- | +| Transaction read-only | `on` | +| Exact Nightingale encounter | 1 | +| Nightingale encounter satisfying every recorded owner/state constraint | 1 | +| Exact Nightingale principal | 1 | +| Principal type/name/state/contact/password/product/synthetic safeguards | satisfied | +| `preferences.provisioning.source_template_product` | `hummingbird_patient` | +| `preferences.provisioning.source_template_owner` | `hummingbird-patient-reference-identity-provisioner-v1` | +| Nightingale identity links/grants/challenges/sessions/audits/notification devices | 0 / 0 / 0 / 0 / 0 / 0 | +| Original Hummingbird encounter/principal/identity-link/access-grant cardinalities | 1 / 1 / 1 / 1 | + +The first form of this confirmation query tested a nonexistent generic +`preferences.provisioning.source_template` path and therefore returned zero for its +combined principal predicate. A metadata-only follow-up established the two exact +source-template keys above; all safety and lineage values were present. No write was +needed or attempted. + +## 7. Operational boundary and follow-up + +The sample is not a pilot cohort member and must never be confused with a real patient. +Specifically: + +- the Nightingale executable contract still has zero paths; +- no Laravel route or container binding can reach the sample; +- native Nightingale remains offline and no-data; +- no clinical or patient-authored content was cloned; +- no relationship or representative access exists; +- no content release or communication capability exists; and +- no feature flag, source adapter, migration, application deployment, or pilot activation + occurred. + +Retirement was not authorized or performed. A future teardown must use the exact owner and +patient-reference markers, prove one/one cardinality and zero dependent identity/session/ +grant rows, and run in its own explicitly authorized transaction. The operational owner +must also decide whether the sample should be refreshed, soft-deleted, or removed after its +verification purpose ends; `expected_discharge_date` alone does not perform cleanup. diff --git a/docs/hummingbird/PATIENT-VISUAL-ASSET-PROVENANCE.md b/docs/hummingbird/PATIENT-VISUAL-ASSET-PROVENANCE.md index 89cb61a0..52c6c032 100644 --- a/docs/hummingbird/PATIENT-VISUAL-ASSET-PROVENANCE.md +++ b/docs/hummingbird/PATIENT-VISUAL-ASSET-PROVENANCE.md @@ -1,9 +1,9 @@ # Hummingbird Patient visual asset provenance -Status: **cleared for distribution — Unsplash License** -Last verified: 2026-07-27 +Status: **release-blocking review open** +Last verified: 2026-07-19 Technical owner: Hummingbird Patient mobile maintainers -Release approval owner: Sanjay Udoshi (repository owner), 2026-07-27 +Release approval owner: Product Design and Legal/Compliance (named approver not yet assigned) This manifest covers the photographic Hummingbird backgrounds bundled in the separate iOS and Android patient applications. It records technical lineage; it @@ -11,27 +11,20 @@ does **not** establish copyright ownership or grant a license. ## Release rule -The four assets below are licensed under the **Unsplash License**, confirmed by -the repository owner on 2026-07-27. The Unsplash License permits free commercial -and non-commercial use without permission; attribution is appreciated but not -required. The prior blanket hold is therefore lifted and these assets are -approved for distribution, including TestFlight and App Store builds. - -One caveat is recorded honestly rather than papered over: the four app-local -copies do **not** hash-match any of the Unsplash-named originals under -`hummingbird/images/`, so the specific photographer and photo ID for each scene -could not be derived mechanically. The license is attested; the per-photo -credit is not yet reconstructed. If Unsplash credit lines are wanted in an -about/licences screen, the original photo IDs need to be supplied. +The four assets below are technically fit for local, offline use, but none is +approved for production distribution until a named release approver records the +copyright source, permitted uses, required attribution, and approval evidence. +The current release status for every row is therefore **hold — licensing and +attribution review required**. ## Asset ledger | Patient scene | Repository source | App-local copies | Copy/crop status | Dimensions / bytes | SHA-256 | Licensing and attribution review | Release owner / status | | ------------------------------------------------------------- | ---------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------ | -------------------------------------------- | -| Calm green / Today | `public/images/auth/hummingbirds/hummingbird-01.jpg` | iOS `Assets.xcassets/PatientCalmGreen.imageset/hummingbird-01.jpg`; Android `drawable-nodpi/patient_hummingbird_calm_green.jpg` | iOS is a byte-for-byte copy. Android is a proportionally resized and recompressed derivative (2000 px high). Neither has a baked crop; runtime aspect-fill crop varies by viewport behind a deterministic readability scrim. | Source + iOS: 1689 x 2400 / 830,035 bytes. Android: 1407 x 2000 / 543,199 bytes. | Source + iOS: `9230a368fd2c0cab308280425b35b645b2a505871277a8136ec1c199cd53d6dc`. Android: `d5dc322481721ac5a29a6fe34c777bf727385dcd9d3d5aad1674182b5ccbec2d`. | **Unsplash License** (owner-attested 2026-07-27). Free commercial and non-commercial use, no permission required, attribution appreciated but not required. Specific photographer/photo ID not reconstructed — see the caveat under Release rule. | Repository owner / **APPROVED** | -| Airy flight / Welcome, authentication, loading, privacy cover | `public/images/auth/hummingbirds/hummingbird-06.jpg` | iOS `Assets.xcassets/PatientAiryFlight.imageset/hummingbird-06.jpg`; Android `drawable-nodpi/patient_hummingbird_airy_flight.jpg` | iOS is a byte-for-byte copy. Android is a proportionally resized and recompressed derivative (2000 px high). Neither has a baked crop; runtime aspect-fill crop varies by viewport behind a deterministic readability scrim. | Source + iOS: 1600 x 2400 / 512,563 bytes. Android: 1333 x 2000 / 345,067 bytes. | Source + iOS: `65b640f035d8527d879f5b354e36eabb78ec3c9ca5c915356e05912a4aadc008`. Android: `a400063289b0ec8b62a7059c3e1e10618da545fe50871b2f3ddb35935e958117`. | **Unsplash License** (owner-attested 2026-07-27). Free commercial and non-commercial use, no permission required, attribution appreciated but not required. Specific photographer/photo ID not reconstructed — see the caveat under Release rule. | Repository owner / **APPROVED** | -| Human connection / Care Team and error states | `public/images/auth/hummingbirds/hummingbird-07.jpg` | iOS `Assets.xcassets/PatientCareConnection.imageset/hummingbird-07.jpg`; Android `drawable-nodpi/patient_hummingbird_care_connection.jpg` | iOS is a byte-for-byte copy. Android is a proportionally resized and recompressed derivative (2000 px high). Neither has a baked crop; runtime aspect-fill crop varies by viewport behind a deterministic readability scrim. | Source + iOS: 1600 x 2400 / 399,380 bytes. Android: 1333 x 2000 / 277,988 bytes. | Source + iOS: `b545c010ed87c9ee4150c616b6031f2e66b329bc0e339fbe4ac735d6f4236988`. Android: `b70ba3d0626f27bd29311a3e70a8e306768fd3c316cfae2554765b89603dcfbd`. | **Unsplash License** (owner-attested 2026-07-27). Free commercial and non-commercial use, no permission required, attribution appreciated but not required. Specific photographer/photo ID not reconstructed — see the caveat under Release rule. | Repository owner / **APPROVED** | -| Warm motion / My Path and empty states | `public/images/auth/hummingbirds/hummingbird-12.jpg` | iOS `Assets.xcassets/PatientWarmMotion.imageset/hummingbird-12.jpg`; Android `drawable-nodpi/patient_hummingbird_warm_motion.jpg` | iOS is a byte-for-byte copy. Android is a proportionally resized and recompressed derivative (2000 px high). Neither has a baked crop; runtime aspect-fill crop varies by viewport behind a deterministic readability scrim. | Source + iOS: 1800 x 2400 / 461,289 bytes. Android: 1500 x 2000 / 319,131 bytes. | Source + iOS: `38e37231c4a14e3223823bbee531590aadd982bc7994c6538b02d291670b729d`. Android: `b3f127a8fbd2754d69adc12432fc01f22a9096ee8850b8fdab10e769e5105fb8`. | **Unsplash License** (owner-attested 2026-07-27). Free commercial and non-commercial use, no permission required, attribution appreciated but not required. Specific photographer/photo ID not reconstructed — see the caveat under Release rule. | Repository owner / **APPROVED** | +| Calm green / Today | `public/images/auth/hummingbirds/hummingbird-01.jpg` | iOS `Assets.xcassets/PatientCalmGreen.imageset/hummingbird-01.jpg`; Android `drawable-nodpi/patient_hummingbird_calm_green.jpg` | iOS is a byte-for-byte copy. Android is a proportionally resized and recompressed derivative (2000 px high). Neither has a baked crop; runtime aspect-fill crop varies by viewport behind a deterministic readability scrim. | Source + iOS: 1689 x 2400 / 830,035 bytes. Android: 1407 x 2000 / 543,199 bytes. | Source + iOS: `9230a368fd2c0cab308280425b35b645b2a505871277a8136ec1c199cd53d6dc`. Android: `d5dc322481721ac5a29a6fe34c777bf727385dcd9d3d5aad1674182b5ccbec2d`. | **Not documented / not approved.** Creator, source URL, license terms, consent/model release (if applicable), and attribution requirement remain unverified. | Product Design + Legal/Compliance / **HOLD** | +| Airy flight / Welcome, authentication, loading, privacy cover | `public/images/auth/hummingbirds/hummingbird-06.jpg` | iOS `Assets.xcassets/PatientAiryFlight.imageset/hummingbird-06.jpg`; Android `drawable-nodpi/patient_hummingbird_airy_flight.jpg` | iOS is a byte-for-byte copy. Android is a proportionally resized and recompressed derivative (2000 px high). Neither has a baked crop; runtime aspect-fill crop varies by viewport behind a deterministic readability scrim. | Source + iOS: 1600 x 2400 / 512,563 bytes. Android: 1333 x 2000 / 345,067 bytes. | Source + iOS: `65b640f035d8527d879f5b354e36eabb78ec3c9ca5c915356e05912a4aadc008`. Android: `a400063289b0ec8b62a7059c3e1e10618da545fe50871b2f3ddb35935e958117`. | **Not documented / not approved.** Creator, source URL, license terms, consent/model release (if applicable), and attribution requirement remain unverified. | Product Design + Legal/Compliance / **HOLD** | +| Human connection / Care Team and error states | `public/images/auth/hummingbirds/hummingbird-07.jpg` | iOS `Assets.xcassets/PatientCareConnection.imageset/hummingbird-07.jpg`; Android `drawable-nodpi/patient_hummingbird_care_connection.jpg` | iOS is a byte-for-byte copy. Android is a proportionally resized and recompressed derivative (2000 px high). Neither has a baked crop; runtime aspect-fill crop varies by viewport behind a deterministic readability scrim. | Source + iOS: 1600 x 2400 / 399,380 bytes. Android: 1333 x 2000 / 277,988 bytes. | Source + iOS: `b545c010ed87c9ee4150c616b6031f2e66b329bc0e339fbe4ac735d6f4236988`. Android: `b70ba3d0626f27bd29311a3e70a8e306768fd3c316cfae2554765b89603dcfbd`. | **Not documented / not approved.** Creator, source URL, license terms, consent/model release (if applicable), and attribution requirement remain unverified. | Product Design + Legal/Compliance / **HOLD** | +| Warm motion / My Path and empty states | `public/images/auth/hummingbirds/hummingbird-12.jpg` | iOS `Assets.xcassets/PatientWarmMotion.imageset/hummingbird-12.jpg`; Android `drawable-nodpi/patient_hummingbird_warm_motion.jpg` | iOS is a byte-for-byte copy. Android is a proportionally resized and recompressed derivative (2000 px high). Neither has a baked crop; runtime aspect-fill crop varies by viewport behind a deterministic readability scrim. | Source + iOS: 1800 x 2400 / 461,289 bytes. Android: 1500 x 2000 / 319,131 bytes. | Source + iOS: `38e37231c4a14e3223823bbee531590aadd982bc7994c6538b02d291670b729d`. Android: `b3f127a8fbd2754d69adc12432fc01f22a9096ee8850b8fdab10e769e5105fb8`. | **Not documented / not approved.** Creator, source URL, license terms, consent/model release (if applicable), and attribution requirement remain unverified. | Product Design + Legal/Compliance / **HOLD** | ## Technical handling @@ -74,9 +67,9 @@ Run from the repository root: ```bash sha256sum \ public/images/auth/hummingbirds/hummingbird-{01,06,07,12}.jpg \ - hummingbird/iosPatientApp/Nightingale/Assets.xcassets/PatientCalmGreen.imageset/hummingbird-01.jpg \ - hummingbird/iosPatientApp/Nightingale/Assets.xcassets/PatientAiryFlight.imageset/hummingbird-06.jpg \ - hummingbird/iosPatientApp/Nightingale/Assets.xcassets/PatientCareConnection.imageset/hummingbird-07.jpg \ - hummingbird/iosPatientApp/Nightingale/Assets.xcassets/PatientWarmMotion.imageset/hummingbird-12.jpg \ + hummingbird/iosPatientApp/HummingbirdPatient/Assets.xcassets/PatientCalmGreen.imageset/hummingbird-01.jpg \ + hummingbird/iosPatientApp/HummingbirdPatient/Assets.xcassets/PatientAiryFlight.imageset/hummingbird-06.jpg \ + hummingbird/iosPatientApp/HummingbirdPatient/Assets.xcassets/PatientCareConnection.imageset/hummingbird-07.jpg \ + hummingbird/iosPatientApp/HummingbirdPatient/Assets.xcassets/PatientWarmMotion.imageset/hummingbird-12.jpg \ hummingbird/androidPatientApp/app/src/main/res/drawable-nodpi/patient_hummingbird_{calm_green,airy_flight,care_connection,warm_motion}.jpg ``` diff --git a/docs/hummingbird/README.md b/docs/hummingbird/README.md index 68c75228..e3110ae7 100644 --- a/docs/hummingbird/README.md +++ b/docs/hummingbird/README.md @@ -1,7 +1,9 @@ -# Hummingbird — Mobile Companion for Zephyrus +# Hummingbird — Staff Mobile Companion for Zephyrus > **Status:** Active implementation — verified repository snapshot 2026-07-23 -> **Scope:** Native mobile companion to the Zephyrus hospital-operations command center. +> **Scope:** Native mobile companion to the Zephyrus hospital-operations command center for +> staff. The patient product is now **Nightingale**; it has an independent development and +> release stream. > **Platforms:** Android (Kotlin / Jetpack Compose) · iOS (Swift / SwiftUI). The clients > currently share contracts and fixtures but still duplicate domain/data logic. The > accepted direction is generated native clients and rules; a shared KMP runtime is @@ -12,11 +14,18 @@ pocket of every healthcare worker — from the transporter moving a patient, to nurse working a unit, to the CMO glancing at house status between meetings. It is the **glance-and-act** surface of Zephyrus, not a port of the desktop dashboards. -> ## 👉 Start here: [**ZEPHYRUS-HUMMINGBIRD-FUNCTIONAL-PARITY-AND-PATIENT-EXPERIENCE-PLAN-2026-07-19.md**](ZEPHYRUS-HUMMINGBIRD-FUNCTIONAL-PARITY-AND-PATIENT-EXPERIENCE-PLAN-2026-07-19.md) +> ## Patient-product direction: [**Nightingale Patient Product Plan**](../plans/nightingale-patient-product-2026-07-26.md) > -> The current repo-grounded execution plan and checklist for staff functional parity plus -> the separately secured inpatient-facing product. The older implementation and reference -> documents remain design history and supporting evidence. +> Nightingale replaces Hummingbird Patient as the dedicated inpatient-facing product. It has +> its own native app IDs, brand, release train, support model, and safety gates. Historical +> Hummingbird Patient evidence remains retained as migration reference; it is not a +> production release vehicle. +> +> ## 👉 Hummingbird staff parity: [**ZEPHYRUS-HUMMINGBIRD-FUNCTIONAL-PARITY-AND-PATIENT-EXPERIENCE-PLAN-2026-07-19.md**](ZEPHYRUS-HUMMINGBIRD-FUNCTIONAL-PARITY-AND-PATIENT-EXPERIENCE-PLAN-2026-07-19.md) +> +> The current repo-grounded execution plan and checklist for Hummingbird Staff functional +> parity. Its historical patient sections remain supporting evidence and are superseded for +> new patient-product work by the Nightingale plan above. > > ## Direction overlay: [**ALTITUDE-PERSONA-OPERATING-PLAN.md**](ALTITUDE-PERSONA-OPERATING-PLAN.md) > @@ -48,13 +57,18 @@ capability as one of: --- +> **Historical patient-reference notice:** statements below about the former Hummingbird +> Patient contract, endpoints, targets, or feature flags are retained as migration evidence. +> They do not authorize new patient work in Hummingbird or describe the current patient +> product direction; that direction is Nightingale. + ## The strategic picture The findings below began with the June 2026 research audit and have been reconciled with the current implementation. Current completion status lives in [`capability-ledger.v1.yaml`](capability-ledger.v1.yaml), with a generated human-readable view in [`generated/capability-coverage.md`](generated/capability-coverage.md). The separate -patient boundary is contract-first: [`api-contract/nightingale.v1.yaml`](api-contract/nightingale.v1.yaml) +patient boundary is contract-first: [`api-contract/hummingbird-patient.v1.yaml`](api-contract/hummingbird-patient.v1.yaml) is checked against the live Laravel route table and owns its operations independently in the capability ledger. Its governance status remains draft and all patient feature flags remain fail-closed by default. The contract currently includes the read-only experience, @@ -146,11 +160,11 @@ that shape this entire plan. Full detail in [research/](research/). - [**ADR-2026-07-19-patient-projection-source-of-truth.md**](ADR-2026-07-19-patient-projection-source-of-truth.md) — defines patient content as a released, versioned projection rather than a shadow clinical record. - [**ADR-2026-07-19-patient-fhir-boundary.md**](ADR-2026-07-19-patient-fhir-boundary.md) — defines explicit FHIR consumption/project dispositions and prohibits patient-product EHR write-back until a separately governed integration is approved. - [**PLATFORM-RECONCILIATION-TODO.md**](PLATFORM-RECONCILIATION-TODO.md) - the Android/iOS parity execution checklist after the Altitude 2.0 and current-code reconciliation review. -- [**ZEPHYRUS-HUMMINGBIRD-FUNCTIONAL-PARITY-AND-PATIENT-EXPERIENCE-PLAN-2026-07-19.md**](ZEPHYRUS-HUMMINGBIRD-FUNCTIONAL-PARITY-AND-PATIENT-EXPERIENCE-PLAN-2026-07-19.md) — the current repo-grounded parity audit and execution plan, including the separately secured inpatient-facing Hummingbird product. +- [**ZEPHYRUS-HUMMINGBIRD-FUNCTIONAL-PARITY-AND-PATIENT-EXPERIENCE-PLAN-2026-07-19.md**](ZEPHYRUS-HUMMINGBIRD-FUNCTIONAL-PARITY-AND-PATIENT-EXPERIENCE-PLAN-2026-07-19.md) — the current repo-grounded Hummingbird Staff parity audit and execution plan. Its patient material remains historical migration evidence; new patient work is governed by the Nightingale plan. - [**capability-ledger.v1.yaml**](capability-ledger.v1.yaml) — the machine-readable owner, disposition, platform, contract, classification, offline, and evidence control plane. - [**generated/capability-coverage.md**](generated/capability-coverage.md) — generated capability inventory and unresolved-state report; counts are not treated as a parity percentage. - [**patient-disclosure-matrix.v1.yaml**](patient-disclosure-matrix.v1.yaml) — CI-validated draft patient disclosure, provenance, freshness, relationship, offline, notification, and prohibited-field rules; governance approval remains pending. -- [**api-contract/nightingale.v1.yaml**](api-contract/nightingale.v1.yaml) — OpenAPI 3.1 patient-realm contract with uniform envelopes, patient bearer abilities, feature gates, release controls, projection semantics, and audit obligations; CI rejects drift from the registered `/api/patient/v1` operations. +- [**api-contract/hummingbird-patient.v1.yaml**](api-contract/hummingbird-patient.v1.yaml) — OpenAPI 3.1 patient-realm contract with uniform envelopes, patient bearer abilities, feature gates, release controls, projection semantics, and audit obligations; CI rejects drift from the registered `/api/patient/v1` operations. **Deep-dive references** (granular detail behind the consolidated plan) @@ -166,7 +180,7 @@ that shape this entire plan. Full detail in [research/](research/). **Scaffolding (begun)** - [design-tokens/](design-tokens/) — the cross-platform token source (DTCG JSON) + Style Dictionary config + sample Compose/SwiftUI outputs. -- [api-contract/](api-contract/) — the worker-facing BFF contract (`hummingbird-bff.v1.yaml`) and separately governed patient product contract (`nightingale.v1.yaml`). +- [api-contract/](api-contract/) — the worker-facing BFF contract (`hummingbird-bff.v1.yaml`) and separately governed patient product contract (`hummingbird-patient.v1.yaml`). - [role-catalog.v1.json](role-catalog.v1.json) — interim shared role, home-kind, feature-route, status, and urgency vocabulary contract used to verify backend/iOS/Android parity. **Backend — Phase 0 (built, pending verification)** diff --git a/docs/hummingbird/TESTFLIGHT.md b/docs/hummingbird/TESTFLIGHT.md index a63d3ee6..ee8351b7 100644 --- a/docs/hummingbird/TESTFLIGHT.md +++ b/docs/hummingbird/TESTFLIGHT.md @@ -1,139 +1,28 @@ -# TestFlight — Zephyrus-Hummingbird & Zephyrus-Nightingale +# Hummingbird staff TestFlight -One script ships both iOS apps: **`./testflight.sh`** at the repo root. +Hummingbird is the staff mobile product. Its iOS distribution source is +`hummingbird/iosApp`, scheme `Hummingbird`, with bundle identifier +`net.acumenus.hummingbird`. -| App Store Connect record | Bundle id | App id | Source | -|---|---|---|---| -| **Zephyrus-Hummingbird** | `net.acumenus.hummingbird` | 6785290384 | `hummingbird/iosApp` (scheme `Hummingbird`) | -| **Zephyrus-Nightingale** | `net.acumenus.nightingale` | 6794950746 | `hummingbird/iosPatientApp` (scheme `Nightingale`) | - -Nightingale is the patient-facing app, and it is Nightingale end to end: the iOS -target, scheme, and project; the Android `applicationId` and package; the backend -config, environment variables, middleware, and services. Nothing patient-facing -carries the Hummingbird name. - -The two directory names — `hummingbird/iosPatientApp` and -`hummingbird/androidPatientApp` — are deliberately kept. They describe platform and -role rather than brand, and they sit under `hummingbird/` because that is where the -mobile clients live, not because the product is Hummingbird. - -## Quick start - -```bash -./testflight.sh doctor # verify credentials, toolchain, signing, ASC reachability -./testflight.sh ship hummingbird # archive → sign → upload -./testflight.sh ship nightingale -./testflight.sh ship all # both, sequentially -``` - -Then watch it land: +The repository-level `./testflight.sh` helper can archive, export, upload, inspect, wait +for, and distribute an authorized Hummingbird build. Its non-secret registry template is +`.appledeploy.example`; real App Store Connect credentials remain outside the repository +in `.appledeploy` and the configured private-key path. ```bash -./testflight.sh status nightingale # defaults to the build you just shipped -./testflight.sh wait nightingale # poll until processing finishes (5-20 min) -./testflight.sh builds hummingbird # recent upload history +./testflight.sh doctor +./testflight.sh ship hummingbird --no-upload +./testflight.sh ship hummingbird --wait --to "Internal" +./testflight.sh builds hummingbird +./testflight.sh status hummingbird ``` -## Flags for `ship` - -| Flag | Effect | -|---|---| -| `--build ` | Explicit build number. Default is a UTC timestamp (`yyyymmddHHMM`). | -| `--no-upload` | Stop at the signed `.ipa` — useful for verifying signing without shipping. | -| `--wait` | Block until App Store Connect finishes processing. | -| `--to ` | After processing, hand the build to a beta tester group. Implies `--wait`. | - -```bash -./testflight.sh ship nightingale --wait --to "Internal" -``` - -## Credentials — `.appledeploy` - -Everything lives in `.appledeploy` at the repo root. It is **gitignored**; -`.appledeploy.example` is the committed template. - -```bash -cp .appledeploy.example .appledeploy # then fill in and run ./testflight.sh doctor -``` - -| Key | Where it comes from | -|---|---| -| `APPLE_TEAM_ID` | Apple Developer → Membership details | -| `ASC_KEY_ID` | App Store Connect → Users & Access → Integrations → App Store Connect API | -| `ASC_ISSUER_ID` | Top of that same page (one per team) | -| `ASC_KEY_PATH` | Where you saved `AuthKey_.p8` | -| `_APP_ID` | Numeric app id — `./testflight.sh apps` prints them | -| `TESTFLIGHT_GROUP` | Optional default tester group for `--to` / `distribute` | - -The **only real secret is the `.p8` private key**, which lives outside the repo -(`~/.appstoreconnect/private_keys/`). It can be downloaded **exactly once** when you -generate it — keep a backup. Losing it means revoking the key and generating a new one. -The API key needs the **App Manager** role to upload. - -## Adding a third app - -Append the slug to `APPS` in `.appledeploy` and add a matching uppercase block: - -```sh -APPS="hummingbird nightingale eddy" - -EDDY_NAME="Zephyrus-Eddy" -EDDY_APP_ID=6800000000 -EDDY_BUNDLE_ID=net.acumenus.eddy -EDDY_DIR=hummingbird/iosEddyApp -EDDY_XCODEPROJ=Eddy.xcodeproj -EDDY_SCHEME=Eddy -``` - -The app directory needs an `ExportOptions.plist` (copy either existing one). No script -changes required. - -## What's already wired - -- **Signing**: automatic, team `TKXPY255A2`. `-allowProvisioningUpdates` creates - profiles on demand — Nightingale's was minted on its first archive. -- **Project generation**: `ship` runs `xcodegen generate` first, so a stale - `.xcodeproj` can never ship the wrong bundle id. Nightingale's `.xcodeproj` is - tracked in git and CI diffs it against `project.yml` - (`scripts/ci/verify-hummingbird-patient-xcode-project.sh`) — commit it after any - `project.yml` change. -- **APNs per config** (Hummingbird only): Debug → `Hummingbird.entitlements` - (`development`); Release → `Hummingbird.production.entitlements` (`production`). - TestFlight requires production. Nightingale has no push entitlement yet. -- **Export compliance**: `ITSAppUsesNonExemptEncryption = false` on both (standard - HTTPS only) — no per-upload prompt. -- **Release backend**: Hummingbird points at `https://zephyrus.acumenus.net` - (`TransportSecurityPolicy.productionHost`). - -## Gotchas +Running the helper is not itself release authorization. Hummingbird release owners must +still verify the exact source SHA, CI result, signing identity, version/build monotonicity, +exported artifact identity, privacy declarations, tester cohort, and go/no-go record. -- **Build numbers must increase** within a marketing version. Both apps ship - `CFBundleShortVersionString = 1.0` (hardcoded in `Info.plist`, which overrides - `MARKETING_VERSION` in `project.yml`). The timestamp default keeps climbing, so this - resolves itself — but if you pass `--build` manually, go higher than the last one - `./testflight.sh builds ` reports. -- **Nightingale's patient API is off by default.** `HBPPatientAPIEnabled = false` and - `HBPPatientAPIBaseURL = ""` in `Info.plist`, so a TestFlight build runs without a - live backend until those are set. Flip them in - `hummingbird/iosPatientApp/project.yml` before a tester-facing build that needs real - data. -- **Processing lag.** A build is not visible to the API for a minute or two after - upload; `status` reports `not visible yet` in that window, which is normal. -- **`altool` can exit 0 on a failed upload.** Its own `--help` claims "0 success, - 1 failure", but on 2026-07-26 App Store Connect's `/v1/apps` endpoint returned - 500s and altool logged `ERROR: [altool] Failed to determine the Apple ID from - Bundle ID ...`, uploaded nothing, and still exited 0. `ship` therefore does not - trust the exit code: it scans altool's output for `ERROR:` **and** confirms the - build is actually visible in App Store Connect before reporting success. If you - ever script around this file, do the same — exit code alone is not evidence. -- **Why `--upload-package`, not `--upload-app`.** `--upload-app` first resolves the - bundle id to an Apple ID via `/v1/apps` — the exact endpoint that outage took - down. Since `.appledeploy` already records each Apple ID, `ship` passes it - directly via `--upload-package` and skips the lookup. That path kept working - throughout the outage. The `--upload-app` fallback only triggers if `APP_ID` is - missing from the config. -- **Apple-side outages** are worth ruling out before debugging your own setup: - . `/v1/builds` stayed healthy while - `/v1/apps` was down, so partial failure is possible. -- **External testers** need a one-time Beta App Review. Internal testers (up to 100 - App Store Connect users) get builds immediately. +Nightingale is a separate patient product with a different source root, release boundary, +and safety gates. Its distribution record is +[Nightingale TestFlight and iOS distribution](../nightingale/TESTFLIGHT.md). The historical +`hummingbird/iosPatientApp` target remains migration evidence and is not a Nightingale or +TestFlight source. diff --git a/docs/hummingbird/api-contract/contract-operations.lock b/docs/hummingbird/api-contract/contract-operations.lock index 7ea8d76d..fcfc3012 100644 --- a/docs/hummingbird/api-contract/contract-operations.lock +++ b/docs/hummingbird/api-contract/contract-operations.lock @@ -1,7 +1,7 @@ # Hummingbird contract operations baseline — APPEND-ONLY. # # Every operation (METHOD full-path [operationId]) published by the two OpenAPI -# contracts (hummingbird-bff.v1.yaml, nightingale.v1.yaml). An operation +# contracts (hummingbird-bff.v1.yaml, hummingbird-patient.v1.yaml). An operation # may only be REMOVED or RENAMED here if it has a record in # contract-breaking-changes.v1.yaml; scripts/verify-hummingbird-contract-baseline.php # fails CI when a baselined operation disappears from the specs without one, or diff --git a/docs/hummingbird/api-contract/nightingale.v1.yaml b/docs/hummingbird/api-contract/hummingbird-patient.v1.yaml similarity index 97% rename from docs/hummingbird/api-contract/nightingale.v1.yaml rename to docs/hummingbird/api-contract/hummingbird-patient.v1.yaml index 1652896a..74e14496 100644 --- a/docs/hummingbird/api-contract/nightingale.v1.yaml +++ b/docs/hummingbird/api-contract/hummingbird-patient.v1.yaml @@ -52,8 +52,8 @@ paths: idempotency_key: unsupported retry: only_after_definitive_transport_failure x-zephyrus-release-policy: - product_flag: NIGHTINGALE_ENABLED - feature_flag: NIGHTINGALE_ENROLLMENT_ENABLED + product_flag: HUMMINGBIRD_PATIENT_ENABLED + feature_flag: HUMMINGBIRD_PATIENT_ENROLLMENT_ENABLED default: disabled disabled_status: 404 governance_status: draft_not_approved @@ -102,8 +102,8 @@ paths: idempotency_key: unsupported retry: only_after_definitive_transport_failure x-zephyrus-release-policy: - product_flag: NIGHTINGALE_ENABLED - feature_flag: NIGHTINGALE_TOKEN_EXCHANGE_ENABLED + product_flag: HUMMINGBIRD_PATIENT_ENABLED + feature_flag: HUMMINGBIRD_PATIENT_TOKEN_EXCHANGE_ENABLED default: disabled disabled_status: 404 governance_status: draft_not_approved @@ -154,8 +154,8 @@ paths: idempotency_key: unsupported retry: never_replay_a_consumed_refresh_credential x-zephyrus-release-policy: - product_flag: NIGHTINGALE_ENABLED - feature_flag: NIGHTINGALE_TOKEN_EXCHANGE_ENABLED + product_flag: HUMMINGBIRD_PATIENT_ENABLED + feature_flag: HUMMINGBIRD_PATIENT_TOKEN_EXCHANGE_ENABLED default: disabled disabled_status: 404 governance_status: draft_not_approved @@ -200,8 +200,8 @@ paths: idempotency_key: unsupported retry: permitted_with_same_credential_until_revoked x-zephyrus-release-policy: - product_flag: NIGHTINGALE_ENABLED - feature_flag: NIGHTINGALE_TOKEN_EXCHANGE_ENABLED + product_flag: HUMMINGBIRD_PATIENT_ENABLED + feature_flag: HUMMINGBIRD_PATIENT_TOKEN_EXCHANGE_ENABLED default: disabled disabled_status: 404 governance_status: draft_not_approved @@ -246,8 +246,8 @@ paths: idempotency_key: not_applicable retry: permitted x-zephyrus-release-policy: - product_flag: NIGHTINGALE_ENABLED - feature_flag: NIGHTINGALE_PROFILE_ENABLED + product_flag: HUMMINGBIRD_PATIENT_ENABLED + feature_flag: HUMMINGBIRD_PATIENT_PROFILE_ENABLED default: disabled disabled_status: 404 governance_status: draft_not_approved @@ -292,8 +292,8 @@ paths: idempotency_key: unsupported retry: permitted_with_same_body x-zephyrus-release-policy: - product_flag: NIGHTINGALE_ENABLED - feature_flag: NIGHTINGALE_PROFILE_ENABLED + product_flag: HUMMINGBIRD_PATIENT_ENABLED + feature_flag: HUMMINGBIRD_PATIENT_PROFILE_ENABLED default: disabled disabled_status: 404 governance_status: draft_not_approved @@ -347,8 +347,8 @@ paths: idempotency_key: unsupported retry: permitted_with_same_device_handle_and_body x-zephyrus-release-policy: - product_flag: NIGHTINGALE_ENABLED - feature_flag: NIGHTINGALE_NOTIFICATION_DEVICES_ENABLED + product_flag: HUMMINGBIRD_PATIENT_ENABLED + feature_flag: HUMMINGBIRD_PATIENT_NOTIFICATION_DEVICES_ENABLED default: disabled disabled_status: 404 governance_status: draft_not_approved @@ -399,8 +399,8 @@ paths: idempotency_key: not_required_for_resource_delete retry: permitted x-zephyrus-release-policy: - product_flag: NIGHTINGALE_ENABLED - feature_flag: NIGHTINGALE_NOTIFICATION_DEVICES_ENABLED + product_flag: HUMMINGBIRD_PATIENT_ENABLED + feature_flag: HUMMINGBIRD_PATIENT_NOTIFICATION_DEVICES_ENABLED default: disabled disabled_status: 404 governance_status: draft_not_approved @@ -447,8 +447,8 @@ paths: idempotency_key: not_applicable retry: permitted x-zephyrus-release-policy: - product_flag: NIGHTINGALE_ENABLED - feature_flag: NIGHTINGALE_SESSION_MANAGEMENT_ENABLED + product_flag: HUMMINGBIRD_PATIENT_ENABLED + feature_flag: HUMMINGBIRD_PATIENT_SESSION_MANAGEMENT_ENABLED default: disabled disabled_status: 404 governance_status: draft_not_approved @@ -496,8 +496,8 @@ paths: idempotency_key: not_required_for_resource_delete retry: permitted x-zephyrus-release-policy: - product_flag: NIGHTINGALE_ENABLED - feature_flag: NIGHTINGALE_SESSION_MANAGEMENT_ENABLED + product_flag: HUMMINGBIRD_PATIENT_ENABLED + feature_flag: HUMMINGBIRD_PATIENT_SESSION_MANAGEMENT_ENABLED default: disabled disabled_status: 404 governance_status: draft_not_approved @@ -544,8 +544,8 @@ paths: idempotency_key: not_applicable retry: permitted x-zephyrus-release-policy: - product_flag: NIGHTINGALE_ENABLED - feature_flag: NIGHTINGALE_ENCOUNTERS_ENABLED + product_flag: HUMMINGBIRD_PATIENT_ENABLED + feature_flag: HUMMINGBIRD_PATIENT_ENCOUNTERS_ENABLED default: disabled disabled_status: 404 governance_status: draft_not_approved @@ -593,8 +593,8 @@ paths: idempotency_key: not_applicable retry: permitted x-zephyrus-release-policy: - product_flag: NIGHTINGALE_ENABLED - feature_flag: NIGHTINGALE_TODAY_ENABLED + product_flag: HUMMINGBIRD_PATIENT_ENABLED + feature_flag: HUMMINGBIRD_PATIENT_TODAY_ENABLED default: disabled disabled_status: 404 governance_status: draft_not_approved @@ -644,8 +644,8 @@ paths: idempotency_key: not_applicable retry: permitted x-zephyrus-release-policy: - product_flag: NIGHTINGALE_ENABLED - feature_flag: NIGHTINGALE_PATHWAY_ENABLED + product_flag: HUMMINGBIRD_PATIENT_ENABLED + feature_flag: HUMMINGBIRD_PATIENT_PATHWAY_ENABLED default: disabled disabled_status: 404 governance_status: draft_not_approved @@ -695,8 +695,8 @@ paths: idempotency_key: not_applicable retry: permitted x-zephyrus-release-policy: - product_flag: NIGHTINGALE_ENABLED - feature_flag: NIGHTINGALE_PATHWAY_ENABLED + product_flag: HUMMINGBIRD_PATIENT_ENABLED + feature_flag: HUMMINGBIRD_PATIENT_PATHWAY_ENABLED default: disabled disabled_status: 404 governance_status: draft_not_approved @@ -746,8 +746,8 @@ paths: idempotency_key: not_applicable retry: permitted x-zephyrus-release-policy: - product_flag: NIGHTINGALE_ENABLED - feature_flag: NIGHTINGALE_PATHWAY_ENABLED + product_flag: HUMMINGBIRD_PATIENT_ENABLED + feature_flag: HUMMINGBIRD_PATIENT_PATHWAY_ENABLED default: disabled disabled_status: 404 governance_status: draft_not_approved @@ -797,8 +797,8 @@ paths: idempotency_key: not_applicable retry: permitted x-zephyrus-release-policy: - product_flag: NIGHTINGALE_ENABLED - feature_flag: NIGHTINGALE_ROUNDS_SUMMARY_ENABLED + product_flag: HUMMINGBIRD_PATIENT_ENABLED + feature_flag: HUMMINGBIRD_PATIENT_ROUNDS_SUMMARY_ENABLED default: disabled disabled_status: 404 governance_status: draft_not_approved @@ -848,8 +848,8 @@ paths: idempotency_key: not_applicable retry: permitted x-zephyrus-release-policy: - product_flag: NIGHTINGALE_ENABLED - feature_flag: NIGHTINGALE_CARE_TEAM_ENABLED + product_flag: HUMMINGBIRD_PATIENT_ENABLED + feature_flag: HUMMINGBIRD_PATIENT_CARE_TEAM_ENABLED default: disabled disabled_status: 404 governance_status: draft_not_approved @@ -899,8 +899,8 @@ paths: idempotency_key: not_applicable retry: permitted x-zephyrus-release-policy: - product_flag: NIGHTINGALE_ENABLED - feature_flag: NIGHTINGALE_MESSAGING_ENABLED + product_flag: HUMMINGBIRD_PATIENT_ENABLED + feature_flag: HUMMINGBIRD_PATIENT_MESSAGING_ENABLED default: disabled disabled_status: 404 governance_status: approved_local_policy_also_required @@ -950,8 +950,8 @@ paths: idempotency_key: not_applicable retry: permitted x-zephyrus-release-policy: - product_flag: NIGHTINGALE_ENABLED - feature_flag: NIGHTINGALE_MESSAGING_ENABLED + product_flag: HUMMINGBIRD_PATIENT_ENABLED + feature_flag: HUMMINGBIRD_PATIENT_MESSAGING_ENABLED default: disabled disabled_status: 404 governance_status: approved_local_policy_also_required @@ -997,20 +997,20 @@ paths: idempotency_key: required_uuid_header retry: same_key_or_client_message_uuid_with_exact_encounter_bound_payload x-zephyrus-release-policy: - product_flag: NIGHTINGALE_ENABLED - feature_flag: NIGHTINGALE_MESSAGING_ENABLED + product_flag: HUMMINGBIRD_PATIENT_ENABLED + feature_flag: HUMMINGBIRD_PATIENT_MESSAGING_ENABLED default: disabled disabled_status: 404 governance_status: approved_local_policy_also_required mutation_readiness: current_message_encryption_key_and_staff_handoff_consumer care_preference_association: topic_code: care_preference - feature_flag: NIGHTINGALE_CARE_PREFERENCES_ENABLED + feature_flag: HUMMINGBIRD_PATIENT_CARE_PREFERENCES_ENABLED default: disabled semantics: patient_authored_nonclinical_preference_not_a_care_plan_order_consent_or_assessment patient_goal_association: topic_code: patient_goal - feature_flag: NIGHTINGALE_GOALS_ENABLED + feature_flag: HUMMINGBIRD_PATIENT_GOALS_ENABLED default: disabled semantics: patient_authored_nonclinical_personal_goal_not_a_clinical_goal_care_plan_order_consent_or_assessment x-zephyrus-error-behavior: @@ -1077,9 +1077,9 @@ paths: idempotency_key: required_uuid_header retry: same_key_or_client_message_uuid_with_exact_released_education_bound_payload x-zephyrus-release-policy: - product_flag: NIGHTINGALE_ENABLED - feature_flag: NIGHTINGALE_TEACH_BACK_ENABLED - prerequisite_feature_flag: NIGHTINGALE_MESSAGING_ENABLED + product_flag: HUMMINGBIRD_PATIENT_ENABLED + feature_flag: HUMMINGBIRD_PATIENT_TEACH_BACK_ENABLED + prerequisite_feature_flag: HUMMINGBIRD_PATIENT_MESSAGING_ENABLED default: disabled disabled_status: 404 governance_status: approved_local_messaging_policy_also_required @@ -1143,8 +1143,8 @@ paths: idempotency_key: not_applicable retry: permitted x-zephyrus-release-policy: - product_flag: NIGHTINGALE_ENABLED - feature_flag: NIGHTINGALE_MESSAGING_ENABLED + product_flag: HUMMINGBIRD_PATIENT_ENABLED + feature_flag: HUMMINGBIRD_PATIENT_MESSAGING_ENABLED default: disabled disabled_status: 404 governance_status: approved_local_policy_also_required @@ -1195,8 +1195,8 @@ paths: idempotency_key: required_uuid_header retry: same_key_or_client_message_uuid_with_exact_thread_bound_payload x-zephyrus-release-policy: - product_flag: NIGHTINGALE_ENABLED - feature_flag: NIGHTINGALE_MESSAGING_ENABLED + product_flag: HUMMINGBIRD_PATIENT_ENABLED + feature_flag: HUMMINGBIRD_PATIENT_MESSAGING_ENABLED default: disabled disabled_status: 404 governance_status: approved_local_policy_also_required @@ -1261,8 +1261,8 @@ paths: idempotency_key: required_uuid_header retry: same_key_or_client_message_uuid_with_exact_action_source_thread_bound_payload x-zephyrus-release-policy: - product_flag: NIGHTINGALE_ENABLED - feature_flag: NIGHTINGALE_MESSAGING_ENABLED + product_flag: HUMMINGBIRD_PATIENT_ENABLED + feature_flag: HUMMINGBIRD_PATIENT_MESSAGING_ENABLED default: disabled disabled_status: 404 governance_status: approved_local_policy_also_required @@ -1328,8 +1328,8 @@ paths: idempotency_key: required_uuid_header retry: same_key_and_exact_payload_only x-zephyrus-release-policy: - product_flag: NIGHTINGALE_ENABLED - feature_flag: NIGHTINGALE_MESSAGING_ENABLED + product_flag: HUMMINGBIRD_PATIENT_ENABLED + feature_flag: HUMMINGBIRD_PATIENT_MESSAGING_ENABLED default: disabled disabled_status: 404 governance_status: approved_local_policy_also_required diff --git a/docs/hummingbird/capability-ledger.v1.yaml b/docs/hummingbird/capability-ledger.v1.yaml index 813abf10..cae829ab 100644 --- a/docs/hummingbird/capability-ledger.v1.yaml +++ b/docs/hummingbird/capability-ledger.v1.yaml @@ -5,7 +5,7 @@ sources: web_routes: routes/web.php api_routes: routes/api.php staff_contract: docs/hummingbird/api-contract/hummingbird-bff.v1.yaml - patient_contract: docs/hummingbird/api-contract/nightingale.v1.yaml + patient_contract: docs/hummingbird/api-contract/hummingbird-patient.v1.yaml role_catalog: docs/hummingbird/role-catalog.v1.json architecture_decisions: native_contracts: docs/hummingbird/ADR-2026-07-19-generated-native-contracts.md @@ -1312,16 +1312,16 @@ capabilities: - tests/Feature/Patient/PatientEducationClarificationApiTest.php - tests/Feature/Patient/PatientCarePreferenceApiTest.php - tests/Feature/Patient/PatientAuthoredGoalApiTest.php - - hummingbird/iosPatientApp/NightingaleTests/PatientAPIBoundaryTests.swift - - hummingbird/iosPatientApp/NightingaleTests/PatientAPIClientTests.swift - - hummingbird/iosPatientApp/NightingaleTests/PatientAPIModelTests.swift - - hummingbird/iosPatientApp/NightingaleTests/PatientAppViewModelTests.swift - - hummingbird/iosPatientApp/NightingaleUITests/PatientSessionManagementUITests.swift - - hummingbird/androidPatientApp/app/src/test/java/net/acumenus/nightingale/data/PatientEndpointBoundaryTest.kt - - hummingbird/androidPatientApp/app/src/test/java/net/acumenus/nightingale/data/PatientSessionCoordinatorTest.kt - - hummingbird/androidPatientApp/app/src/test/java/net/acumenus/nightingale/data/PatientSessionManagementDecoderTest.kt - - hummingbird/androidPatientApp/app/src/test/java/net/acumenus/nightingale/PatientAppViewModelTest.kt - - hummingbird/androidPatientApp/app/src/androidTest/java/net/acumenus/nightingale/PatientSessionManagementInstrumentedTest.kt + - hummingbird/iosPatientApp/HummingbirdPatientTests/PatientAPIBoundaryTests.swift + - hummingbird/iosPatientApp/HummingbirdPatientTests/PatientAPIClientTests.swift + - hummingbird/iosPatientApp/HummingbirdPatientTests/PatientAPIModelTests.swift + - hummingbird/iosPatientApp/HummingbirdPatientTests/PatientAppViewModelTests.swift + - hummingbird/iosPatientApp/HummingbirdPatientUITests/PatientSessionManagementUITests.swift + - hummingbird/androidPatientApp/app/src/test/java/net/acumenus/hummingbird/patient/data/PatientEndpointBoundaryTest.kt + - hummingbird/androidPatientApp/app/src/test/java/net/acumenus/hummingbird/patient/data/PatientSessionCoordinatorTest.kt + - hummingbird/androidPatientApp/app/src/test/java/net/acumenus/hummingbird/patient/data/PatientSessionManagementDecoderTest.kt + - hummingbird/androidPatientApp/app/src/test/java/net/acumenus/hummingbird/patient/PatientAppViewModelTest.kt + - hummingbird/androidPatientApp/app/src/androidTest/java/net/acumenus/hummingbird/patient/PatientSessionManagementInstrumentedTest.kt web_routes: [] staff_bff_operations: [] patient_api_operations: @@ -1351,15 +1351,15 @@ capabilities: - "POST /api/patient/v1/threads/{threadUuid}/messages/{messageUuid}/amend" - "POST /api/patient/v1/threads/{threadUuid}/close" ios_routes: - - hummingbird/iosPatientApp/Nightingale/App/PatientRootView.swift - - hummingbird/iosPatientApp/Nightingale/Features/Today/PatientTodayView.swift - - hummingbird/iosPatientApp/Nightingale/Features/Path/PatientPathView.swift - - hummingbird/iosPatientApp/Nightingale/Features/CareTeam/PatientCareTeamView.swift - - hummingbird/iosPatientApp/Nightingale/Features/Account/PatientSessionManagementView.swift + - hummingbird/iosPatientApp/HummingbirdPatient/App/PatientRootView.swift + - hummingbird/iosPatientApp/HummingbirdPatient/Features/Today/PatientTodayView.swift + - hummingbird/iosPatientApp/HummingbirdPatient/Features/Path/PatientPathView.swift + - hummingbird/iosPatientApp/HummingbirdPatient/Features/CareTeam/PatientCareTeamView.swift + - hummingbird/iosPatientApp/HummingbirdPatient/Features/Account/PatientSessionManagementView.swift android_routes: - - hummingbird/androidPatientApp/app/src/main/java/net/acumenus/nightingale/ui/PatientApp.kt - - hummingbird/androidPatientApp/app/src/main/java/net/acumenus/nightingale/ui/PatientExperienceScreen.kt - - hummingbird/androidPatientApp/app/src/main/java/net/acumenus/nightingale/ui/PatientSessionManagementScreen.kt + - hummingbird/androidPatientApp/app/src/main/java/net/acumenus/hummingbird/patient/ui/PatientApp.kt + - hummingbird/androidPatientApp/app/src/main/java/net/acumenus/hummingbird/patient/ui/PatientExperienceScreen.kt + - hummingbird/androidPatientApp/app/src/main/java/net/acumenus/hummingbird/patient/ui/PatientSessionManagementScreen.kt notification_events: [] offline_class: NO_CACHE data_classification: phi diff --git a/docs/hummingbird/generated/capability-coverage.md b/docs/hummingbird/generated/capability-coverage.md index b251e20e..a9e19029 100644 --- a/docs/hummingbird/generated/capability-coverage.md +++ b/docs/hummingbird/generated/capability-coverage.md @@ -51,7 +51,7 @@ | `mobile_foundation` | `mobile.auth_session` — Staff authentication and session lifecycle | `partial` | `NATIVE` | `staff_p1` | Hummingbird Product | Hummingbird Platform | — | `POST /api/auth/token`
`POST /api/auth/token/refresh`
`POST /api/auth/token/revoke`
`POST /api/auth/change-password`
`GET /api/mobile/v1/me/sessions`
`DELETE /api/mobile/v1/me/sessions/{sessionUuid}` | — | `hummingbird/iosApp/Hummingbird/Features/Auth/LoginView.swift`
`hummingbird/iosApp/Hummingbird/Features/Profile/StaffSessionsView.swift` | `hummingbird/androidApp/app/src/main/java/net/acumenus/hummingbird/ui/LoginScreen.kt`
`hummingbird/androidApp/app/src/main/java/net/acumenus/hummingbird/ui/StaffSessionsScreen.kt` | | `mobile_foundation` | `mobile.for_you` — Universal role-aware For You queue | `partial` | `NATIVE` | `staff_p1` | Hummingbird Product | Hummingbird Platform | — | `GET /api/mobile/v1/for-you` | — | `hummingbird/iosApp/Hummingbird/Features/ForYou/ForYouView.swift`
`hummingbird/iosApp/Hummingbird/Features/ForYou/PatientCommunicationForYouRow.swift` | `hummingbird/androidApp/app/src/main/java/net/acumenus/hummingbird/ui/ForYouScreen.kt` | | `mobile_foundation` | `mobile.profile_devices_realtime` — Staff profile, device registration, and realtime configuration | `partial` | `NATIVE` | `staff_p1` | Hummingbird Product | Hummingbird Platform | — | `GET /api/mobile/v1/me`
`PUT /api/mobile/v1/me/preferences`
`POST /api/mobile/v1/devices`
`DELETE /api/mobile/v1/devices/{device}`
`GET /api/mobile/v1/realtime/config` | — | `hummingbird/iosApp/Hummingbird/Features/Profile/ProfileView.swift` | `hummingbird/androidApp/app/src/main/java/net/acumenus/hummingbird/ui/MainScreen.kt` | -| `patient_experience` | `patient.experience` — Separate inpatient-facing Hummingbird product | `partial` | `PATIENT` | `patient_p3` | Patient Experience Product | Patient Experience Platform | — | — | `POST /api/patient/v1/auth/enroll/challenge/verify`
`POST /api/patient/v1/auth/token`
`POST /api/patient/v1/auth/token/refresh`
`POST /api/patient/v1/auth/token/revoke`
`GET /api/patient/v1/me`
`PUT /api/patient/v1/me/preferences`
`PUT /api/patient/v1/me/notification-devices/{deviceUuid}`
`DELETE /api/patient/v1/me/notification-devices/{deviceUuid}`
`GET /api/patient/v1/me/sessions`
`DELETE /api/patient/v1/me/sessions/{sessionUuid}`
`GET /api/patient/v1/encounters`
`GET /api/patient/v1/encounters/{encounterUuid}/today`
`GET /api/patient/v1/encounters/{encounterUuid}/pathway`
`GET /api/patient/v1/encounters/{encounterUuid}/pathway/events`
`GET /api/patient/v1/encounters/{encounterUuid}/discharge-readiness`
`GET /api/patient/v1/encounters/{encounterUuid}/rounds/summary`
`GET /api/patient/v1/encounters/{encounterUuid}/care-team`
`GET /api/patient/v1/encounters/{encounterUuid}/message-topics`
`GET /api/patient/v1/encounters/{encounterUuid}/threads`
`POST /api/patient/v1/encounters/{encounterUuid}/threads`
`POST /api/patient/v1/encounters/{encounterUuid}/education/{educationItemUuid}/clarifications`
`GET /api/patient/v1/threads/{threadUuid}`
`POST /api/patient/v1/threads/{threadUuid}/messages`
`POST /api/patient/v1/threads/{threadUuid}/messages/{messageUuid}/amend`
`POST /api/patient/v1/threads/{threadUuid}/close` | `hummingbird/iosPatientApp/Nightingale/App/PatientRootView.swift`
`hummingbird/iosPatientApp/Nightingale/Features/Today/PatientTodayView.swift`
`hummingbird/iosPatientApp/Nightingale/Features/Path/PatientPathView.swift`
`hummingbird/iosPatientApp/Nightingale/Features/CareTeam/PatientCareTeamView.swift`
`hummingbird/iosPatientApp/Nightingale/Features/Account/PatientSessionManagementView.swift` | `hummingbird/androidPatientApp/app/src/main/java/net/acumenus/nightingale/ui/PatientApp.kt`
`hummingbird/androidPatientApp/app/src/main/java/net/acumenus/nightingale/ui/PatientExperienceScreen.kt`
`hummingbird/androidPatientApp/app/src/main/java/net/acumenus/nightingale/ui/PatientSessionManagementScreen.kt` | +| `patient_experience` | `patient.experience` — Separate inpatient-facing Hummingbird product | `partial` | `PATIENT` | `patient_p3` | Patient Experience Product | Patient Experience Platform | — | — | `POST /api/patient/v1/auth/enroll/challenge/verify`
`POST /api/patient/v1/auth/token`
`POST /api/patient/v1/auth/token/refresh`
`POST /api/patient/v1/auth/token/revoke`
`GET /api/patient/v1/me`
`PUT /api/patient/v1/me/preferences`
`PUT /api/patient/v1/me/notification-devices/{deviceUuid}`
`DELETE /api/patient/v1/me/notification-devices/{deviceUuid}`
`GET /api/patient/v1/me/sessions`
`DELETE /api/patient/v1/me/sessions/{sessionUuid}`
`GET /api/patient/v1/encounters`
`GET /api/patient/v1/encounters/{encounterUuid}/today`
`GET /api/patient/v1/encounters/{encounterUuid}/pathway`
`GET /api/patient/v1/encounters/{encounterUuid}/pathway/events`
`GET /api/patient/v1/encounters/{encounterUuid}/discharge-readiness`
`GET /api/patient/v1/encounters/{encounterUuid}/rounds/summary`
`GET /api/patient/v1/encounters/{encounterUuid}/care-team`
`GET /api/patient/v1/encounters/{encounterUuid}/message-topics`
`GET /api/patient/v1/encounters/{encounterUuid}/threads`
`POST /api/patient/v1/encounters/{encounterUuid}/threads`
`POST /api/patient/v1/encounters/{encounterUuid}/education/{educationItemUuid}/clarifications`
`GET /api/patient/v1/threads/{threadUuid}`
`POST /api/patient/v1/threads/{threadUuid}/messages`
`POST /api/patient/v1/threads/{threadUuid}/messages/{messageUuid}/amend`
`POST /api/patient/v1/threads/{threadUuid}/close` | `hummingbird/iosPatientApp/HummingbirdPatient/App/PatientRootView.swift`
`hummingbird/iosPatientApp/HummingbirdPatient/Features/Today/PatientTodayView.swift`
`hummingbird/iosPatientApp/HummingbirdPatient/Features/Path/PatientPathView.swift`
`hummingbird/iosPatientApp/HummingbirdPatient/Features/CareTeam/PatientCareTeamView.swift`
`hummingbird/iosPatientApp/HummingbirdPatient/Features/Account/PatientSessionManagementView.swift` | `hummingbird/androidPatientApp/app/src/main/java/net/acumenus/hummingbird/patient/ui/PatientApp.kt`
`hummingbird/androidPatientApp/app/src/main/java/net/acumenus/hummingbird/patient/ui/PatientExperienceScreen.kt`
`hummingbird/androidPatientApp/app/src/main/java/net/acumenus/hummingbird/patient/ui/PatientSessionManagementScreen.kt` | | `patient_experience` | `patient_communications.staff_response` — Accountable staff inbox and response lifecycle for inpatient communications | `partial` | `NATIVE` | `patient_p3` | Patient Experience Product | Hummingbird Platform | `/patient-communications` | `GET /api/mobile/v1/patient-communications/inbox`
`GET /api/mobile/v1/patient-communications/threads/{workItemUuid}`
`GET /api/mobile/v1/patient-communications/threads/{workItemUuid}/route-candidates`
`POST /api/mobile/v1/patient-communications/threads/{workItemUuid}/claim`
`POST /api/mobile/v1/patient-communications/threads/{workItemUuid}/reply`
`POST /api/mobile/v1/patient-communications/threads/{workItemUuid}/close`
`POST /api/mobile/v1/patient-communications/threads/{workItemUuid}/release`
`POST /api/mobile/v1/patient-communications/threads/{workItemUuid}/reassign`
`POST /api/mobile/v1/patient-communications/threads/{workItemUuid}/reroute` | — | `hummingbird/iosApp/Hummingbird/Features/PatientCommunications/PatientCommunicationsView.swift`
`hummingbird/iosApp/Hummingbird/Features/PatientCommunications/PatientCommunicationDetailView.swift` | `hummingbird/androidApp/app/src/main/java/net/acumenus/hummingbird/ui/communications/PatientCommunicationsScreens.kt` | | `perioperative` | `perioperative.analytics` — Perioperative performance analytics | `planned` | `DEEPLINK` | `staff_p3` | Perioperative Product | Web Platform | `/analytics/block-utilization`
`/analytics/or-utilization`
`/analytics/primetime-utilization`
`/analytics/room-running`
`/analytics/turnover-times` | — | — | — | — | | `perioperative` | `perioperative.operations` — OR room, block, and case operations | `partial` | `NATIVE` | `staff_p2` | Perioperative Product | Hummingbird Platform | `/operations/room-status`
`/operations/block-schedule`
`/operations/cases` | `GET /api/mobile/v1/or/board` | — | `hummingbird/iosApp/Hummingbird/Features/OR/ORBoardView.swift` | `hummingbird/androidApp/app/src/main/java/net/acumenus/hummingbird/ui/or/ORScreens.kt` | diff --git a/docs/hummingbird/patient-accessibility-acceptance-matrix.v1.yaml b/docs/hummingbird/patient-accessibility-acceptance-matrix.v1.yaml index af5b608d..f5f44696 100644 --- a/docs/hummingbird/patient-accessibility-acceptance-matrix.v1.yaml +++ b/docs/hummingbird/patient-accessibility-acceptance-matrix.v1.yaml @@ -36,12 +36,12 @@ criteria: remain available at the largest supported size. automation_status: automated_evidence_exists automated_evidence: - - hummingbird/iosPatientApp/Nightingale/DesignSystem/PatientPresentationPreferences.swift - - hummingbird/iosPatientApp/NightingaleTests/PatientPresentationPreferencesTests.swift - - hummingbird/iosPatientApp/NightingaleUITests/PatientSessionManagementUITests.swift - - hummingbird/androidPatientApp/app/src/main/java/net/acumenus/nightingale/ui/PatientPresentationAccessibility.kt - - hummingbird/androidPatientApp/app/src/test/java/net/acumenus/nightingale/ui/PatientPresentationAccessibilityTest.kt - - hummingbird/androidPatientApp/app/src/androidTest/java/net/acumenus/nightingale/PatientSessionManagementInstrumentedTest.kt + - hummingbird/iosPatientApp/HummingbirdPatient/DesignSystem/PatientPresentationPreferences.swift + - hummingbird/iosPatientApp/HummingbirdPatientTests/PatientPresentationPreferencesTests.swift + - hummingbird/iosPatientApp/HummingbirdPatientUITests/PatientSessionManagementUITests.swift + - hummingbird/androidPatientApp/app/src/main/java/net/acumenus/hummingbird/patient/ui/PatientPresentationAccessibility.kt + - hummingbird/androidPatientApp/app/src/test/java/net/acumenus/hummingbird/patient/ui/PatientPresentationAccessibilityTest.kt + - hummingbird/androidPatientApp/app/src/androidTest/java/net/acumenus/hummingbird/patient/PatientSessionManagementInstrumentedTest.kt human_validation_required: - Verify every primary journey at iOS Accessibility XXXL and Android 200 percent font scale with a patient advisor. - Verify no clipped labels, hidden destructive actions, inaccessible confirmation, or horizontal-only recovery path. @@ -57,11 +57,11 @@ criteria: readable care content as text size or contrast needs increase. automation_status: automated_evidence_exists automated_evidence: - - hummingbird/iosPatientApp/Nightingale/DesignSystem/PatientPhotoBackground.swift - - hummingbird/iosPatientApp/NightingaleUITests/PatientReferenceJourneyUITests.swift - - hummingbird/androidPatientApp/app/src/main/java/net/acumenus/nightingale/ui/PatientScenicBackground.kt - - hummingbird/androidPatientApp/app/src/test/java/net/acumenus/nightingale/ui/PatientVisualAssetPolicyTest.kt - - hummingbird/androidPatientApp/app/src/androidTest/java/net/acumenus/nightingale/PatientSessionManagementInstrumentedTest.kt + - hummingbird/iosPatientApp/HummingbirdPatient/DesignSystem/PatientPhotoBackground.swift + - hummingbird/iosPatientApp/HummingbirdPatientUITests/PatientReferenceJourneyUITests.swift + - hummingbird/androidPatientApp/app/src/main/java/net/acumenus/hummingbird/patient/ui/PatientScenicBackground.kt + - hummingbird/androidPatientApp/app/src/test/java/net/acumenus/hummingbird/patient/ui/PatientVisualAssetPolicyTest.kt + - hummingbird/androidPatientApp/app/src/androidTest/java/net/acumenus/hummingbird/patient/PatientSessionManagementInstrumentedTest.kt human_validation_required: - Inspect each image moment with large text, dark mode, light mode, increased contrast, and reduced transparency. - Confirm no image is announced by VoiceOver or TalkBack and no care meaning depends on it. @@ -77,12 +77,12 @@ criteria: than color, motion, or icon alone. automation_status: automated_evidence_exists automated_evidence: - - hummingbird/iosPatientApp/Nightingale/DesignSystem/PatientComponents.swift - - hummingbird/iosPatientApp/Nightingale/DesignSystem/PatientPhotoBackground.swift - - hummingbird/iosPatientApp/NightingaleUITests/PatientSessionManagementUITests.swift - - hummingbird/androidPatientApp/app/src/main/java/net/acumenus/nightingale/ui/NightingaleTheme.kt - - hummingbird/androidPatientApp/app/src/main/java/net/acumenus/nightingale/ui/PatientScenicBackground.kt - - hummingbird/androidPatientApp/app/src/test/java/net/acumenus/nightingale/ui/PatientVisualAssetPolicyTest.kt + - hummingbird/iosPatientApp/HummingbirdPatient/DesignSystem/PatientComponents.swift + - hummingbird/iosPatientApp/HummingbirdPatient/DesignSystem/PatientPhotoBackground.swift + - hummingbird/iosPatientApp/HummingbirdPatientUITests/PatientSessionManagementUITests.swift + - hummingbird/androidPatientApp/app/src/main/java/net/acumenus/hummingbird/patient/ui/HummingbirdPatientTheme.kt + - hummingbird/androidPatientApp/app/src/main/java/net/acumenus/hummingbird/patient/ui/PatientScenicBackground.kt + - hummingbird/androidPatientApp/app/src/test/java/net/acumenus/hummingbird/patient/ui/PatientVisualAssetPolicyTest.kt human_validation_required: - Measure foreground, focus, icon, control-boundary, and status contrast in both themes and high-contrast mode. - Verify a participant can distinguish planned, current, delayed, completed, unavailable, and error states without color perception. @@ -98,10 +98,10 @@ criteria: system reduced-motion setting where available. automation_status: automated_evidence_exists automated_evidence: - - hummingbird/iosPatientApp/Nightingale/App/NightingaleApp.swift - - hummingbird/iosPatientApp/Nightingale/DesignSystem/PatientPhotoBackground.swift - - hummingbird/androidPatientApp/app/src/main/java/net/acumenus/nightingale/ui/PatientPresentationAccessibility.kt - - hummingbird/androidPatientApp/app/src/main/java/net/acumenus/nightingale/ui/PatientPreferencesScreen.kt + - hummingbird/iosPatientApp/HummingbirdPatient/App/HummingbirdPatientApp.swift + - hummingbird/iosPatientApp/HummingbirdPatient/DesignSystem/PatientPhotoBackground.swift + - hummingbird/androidPatientApp/app/src/main/java/net/acumenus/hummingbird/patient/ui/PatientPresentationAccessibility.kt + - hummingbird/androidPatientApp/app/src/main/java/net/acumenus/hummingbird/patient/ui/PatientPreferencesScreen.kt human_validation_required: - Validate saved and operating-system motion settings on supported iOS and Android releases, including launch, loading, navigation, and error recovery. - Confirm reduced motion does not suppress a state, warning, confirmation, or required action. @@ -118,11 +118,11 @@ criteria: continuous individual availability. automation_status: automated_evidence_exists automated_evidence: - - hummingbird/iosPatientApp/Nightingale/Features/Messages/PatientMessagesView.swift - - hummingbird/iosPatientApp/Nightingale/Features/CareTeam/PatientCareTeamView.swift - - hummingbird/iosPatientApp/NightingaleUITests/PatientReferenceJourneyUITests.swift - - hummingbird/androidPatientApp/app/src/main/java/net/acumenus/nightingale/ui/PatientMessagingPanel.kt - - hummingbird/androidPatientApp/app/src/androidTest/java/net/acumenus/nightingale/PatientPrimaryJourneyInstrumentedTest.kt + - hummingbird/iosPatientApp/HummingbirdPatient/Features/Messages/PatientMessagesView.swift + - hummingbird/iosPatientApp/HummingbirdPatient/Features/CareTeam/PatientCareTeamView.swift + - hummingbird/iosPatientApp/HummingbirdPatientUITests/PatientReferenceJourneyUITests.swift + - hummingbird/androidPatientApp/app/src/main/java/net/acumenus/hummingbird/patient/ui/PatientMessagingPanel.kt + - hummingbird/androidPatientApp/app/src/androidTest/java/net/acumenus/hummingbird/patient/PatientPrimaryJourneyInstrumentedTest.kt human_validation_required: - Test urgent guidance comprehension with patients and family advisors, including low literacy, high anxiety, and limited-English-proficiency scenarios. - Verify bedside call-button wording and local emergency policy with each pilot facility before enabling messaging. @@ -138,10 +138,10 @@ criteria: where appropriate, and do not expose internal technical details. automation_status: automated_evidence_exists automated_evidence: - - hummingbird/iosPatientApp/Nightingale/Features/Authentication/PatientWelcomeView.swift - - hummingbird/iosPatientApp/NightingaleUITests/PatientReferenceJourneyUITests.swift - - hummingbird/androidPatientApp/app/src/main/java/net/acumenus/nightingale/ui/PatientAuthenticationScreen.kt - - hummingbird/androidPatientApp/app/src/androidTest/java/net/acumenus/nightingale/PatientPrimaryJourneyInstrumentedTest.kt + - hummingbird/iosPatientApp/HummingbirdPatient/Features/Authentication/PatientWelcomeView.swift + - hummingbird/iosPatientApp/HummingbirdPatientUITests/PatientReferenceJourneyUITests.swift + - hummingbird/androidPatientApp/app/src/main/java/net/acumenus/hummingbird/patient/ui/PatientAuthenticationScreen.kt + - hummingbird/androidPatientApp/app/src/androidTest/java/net/acumenus/hummingbird/patient/PatientPrimaryJourneyInstrumentedTest.kt human_validation_required: - Validate announcements, reading order, retry language, and safe alternative instructions with VoiceOver and TalkBack. - Validate source-outage and stale-data language with clinical safety and patient advisors. @@ -157,9 +157,9 @@ criteria: patient-specific detail. automation_status: automated_evidence_exists automated_evidence: - - hummingbird/iosPatientApp/Nightingale/Privacy/PatientPrivacyCoverView.swift - - hummingbird/iosPatientApp/NightingaleUITests/PatientReferenceJourneyUITests.swift - - hummingbird/androidPatientApp/app/src/androidTest/java/net/acumenus/nightingale/PatientPrimaryJourneyInstrumentedTest.kt + - hummingbird/iosPatientApp/HummingbirdPatient/Privacy/PatientPrivacyCoverView.swift + - hummingbird/iosPatientApp/HummingbirdPatientUITests/PatientReferenceJourneyUITests.swift + - hummingbird/androidPatientApp/app/src/androidTest/java/net/acumenus/hummingbird/patient/PatientPrimaryJourneyInstrumentedTest.kt human_validation_required: - Verify app-switcher, lock, split-screen, accessibility-overlay, and interrupted-login behavior on each supported device family. release_boundary: Privacy and mobile-security review must approve the device-state evidence before a pilot. diff --git a/docs/nightingale/BACKGROUND-ASSET-GOVERNANCE-AND-NATIVE-INTEGRATION-2026-07-26.md b/docs/nightingale/BACKGROUND-ASSET-GOVERNANCE-AND-NATIVE-INTEGRATION-2026-07-26.md new file mode 100644 index 00000000..5ae587e4 --- /dev/null +++ b/docs/nightingale/BACKGROUND-ASSET-GOVERNANCE-AND-NATIVE-INTEGRATION-2026-07-26.md @@ -0,0 +1,179 @@ +# Nightingale background asset governance and native integration + +**Status:** implemented and locally verified for the offline native foundation; external +distribution rights, named human review, signed-store artifacts, and exact-SHA CI for the +current change remain open + +**Decision date:** 2026-07-26 + +**Applies to:** `nightingale/backgrounds`, the Nightingale iOS application, and the +Nightingale Android application + +**Does not authorize:** patient data, identity, networking, clinical content, production +access, patient creation, pilot enrollment, marketing use, store distribution, deployment, +or release + +## 1. Product direction and decision + +The seven images supplied under `nightingale/backgrounds` are the Nightingale product +background collection. Both native products consume one governed derivative set. A +platform-specific photo set, legacy Hummingbird Patient scene taxonomy, downloaded runtime +media, clinical-state mapping, or species-label experience is prohibited. + +The background is atmosphere only. It never communicates diagnosis, progress, acuity, +urgency, care-team action, pathway stage, expected outcome, or whether information is +current. Every patient-visible state and action must remain complete when all decorative +imagery is removed. + +## 2. Immutable source and derivative lineage + +The machine-readable source of record is +[`nightingale/backgrounds/backgrounds.v1.json`](../../nightingale/backgrounds/backgrounds.v1.json). +It binds each supplied source by filename, SHA-256, byte count, and dimensions, then binds +the exact app derivative by repository path, SHA-256, byte count, and dimensions. + +| ID | Source dimensions | Derivative dimensions | Derivative bytes | Derivative SHA-256 | +| --- | ----------------: | --------------------: | ---------------: | ------------------------------------------------------------------ | +| 01 | 1400×1980 | 1400×1980 | 285,047 | `4a741d9d3add77eac8aad8071bf3c9945bbd2ce4aa0d93b0daa79efe166b30b4` | +| 02 | 800×1169 | 800×1169 | 149,373 | `6f2888ae489e2ca2f268065fc7f3f029a051921b4290200e740e455c17cc3510` | +| 03 | 2667×4000 | 1600×2400 | 591,858 | `760c01a4ee1a6830d8a975b5626dd4e452702639a562be875dd2e7838d5334d4` | +| 04 | 2667×4000 | 1600×2400 | 788,373 | `16addb872da986ee99aa3de4db4715f4ff8dc945170934a33eb1192fe3bfb2a3` | +| 05 | 2400×3600 | 1600×2400 | 830,081 | `339ab8239cab04fa6cac373a00dc6993e30a6b3786d35e065b3e810ce6f52e8d` | +| 06 | 3456×5184 | 1600×2400 | 1,110,350 | `e6d9d06cf85ef9360186762e9c0ce0c2297cd6ec445f8409eaa5df49b99086d9` | +| 07 | 5304×6630 | 1920×2400 | 684,892 | `024349a43146b2682af268f6ae4abe14be7725b04456eaf478cda00869086f2c` | + +The exact seven derivatives total 4,439,974 bytes. Sources at or below a 2400-pixel long +edge retain their dimensions; larger sources are downsampled without upscaling. The +committed JPEGs are quality-82, optimized, progressive, and stripped with +`jpegtran -copy none`. EXIF, XMP, Photoshop/IPTC, comment, and GPS-capable application +markers are prohibited. + +The approximately 33 MB source binaries are not duplicated in this development stream. +Their exact fingerprints remain in the manifest. Before external distribution, the release +owner must identify a durable source archive and attach the applicable license or +attribution evidence to the release decision. + +## 3. Cross-platform runtime contract + +### 3.1 Exact catalog + +Both apps contain only these seven logical resource names: + +1. `nightingale_background_01` +2. `nightingale_background_02` +3. `nightingale_background_03` +4. `nightingale_background_04` +5. `nightingale_background_05` +6. `nightingale_background_06` +7. `nightingale_background_07` + +Android compiles the shared `optimized/drawable-nodpi` directory through a dedicated +source-set resource root. iOS packages the same files as loose bundle resources from the +shared directory through the generated Xcode project. Neither target contains a copied or +independently recompressed platform variant. + +### 3.2 Stable local-day selection + +Both platforms derive the catalog index as: + +```text +floorMod(local Gregorian epoch day since 1970-01-01, 7) +``` + +The selected photo therefore remains stable throughout the patient’s local day and maps to +the same catalog entry on iOS and Android. A negative epoch day also uses mathematical +floor modulo, so pre-1970 dates cannot produce an invalid index. + +There is no timer-driven rotation, carousel, video, parallax, pan/zoom, care-event +transition, or automatic cross-fade cycle. A patient-triggered imagery preference change +may use the existing short presentation transition unless reduced motion is effective. + +### 3.3 Accessibility and comprehension + +- The photo is hidden from the accessibility tree and does not receive input. +- No filename, species, visual description, or inferred state is announced. +- “Hide decorative imagery” removes every background photo and decorative product mark + while preserving all text, headings, controls, focus order, and actions. +- iOS also withholds photos when Reduce Transparency or Increased Contrast is active. +- Android withholds photos when the supported high-contrast signal is active. +- Reduced motion disables the current preference transition on both platforms. +- Accessible text size increases the iOS scrim strength; text remains on governed content + surfaces rather than directly over raw pixels. + +### 3.4 Legibility and visual restraint + +The image fills the viewport with a deterministic centered crop. A system-surface gradient +separates photography from the content layer. Patient-readable cards use opaque or +near-opaque system surfaces; stronger system contrast/transparency settings force full +opacity. The background can establish a calm visual character but cannot reduce the +prominence of privacy, urgent-help, stale/withheld, error, or action language when those +future states are approved. + +## 4. Mechanical enforcement + +`scripts/ci/verify-nightingale-background-assets.mjs` fails closed on: + +- a missing, extra, reordered, renamed, malformed, non-progressive, or non-portrait file; +- any source or derivative lineage field that is missing or malformed; +- derivative byte, dimension, or SHA-256 drift; +- upscaling or a derivative long edge above 2400 pixels; +- duplicate IDs, paths, source hashes, or derivative hashes; +- EXIF/XMP/IPTC/Photoshop/comment-capable metadata markers; +- a change from decorative-only, no-clinical-semantics, no-species-label, no-autonomous- + motion behavior; +- removal of imagery suppression, accessibility hiding, or readable-surface requirements; +- weakening the foundation-only rights status; or +- changing the shared local-Gregorian-epoch-day selection rule. + +The verifier includes negative self-tests that deliberately mutate the catalog and require +rejection. The native product-boundary verifier invokes this asset verifier so a +background-only drift cannot bypass the Nightingale safety gate. + +Release artifact verifiers independently prove that the built iOS app and Android APK +contain the exact seven derivative hashes and no ungoverned background file. Native unit +tests prove catalog cardinality, resource readability, selection stability, next-day +rotation, wraparound, negative-day handling, and presentation suppression policy. + +## 5. Native verification disposition + +Local verification includes: + +- iOS Debug build-for-testing, nine XCTest unit tests, four XCUITest journeys, and unsigned + Release simulator build; +- Android Debug/Release unit suites, Debug/Release assemblies, Release artifact inspection, + and seven API 35 instrumentation journeys; +- an iOS Simulator visual inspection showing a governed photo behind strong scrims and + readable content surfaces; +- Android API 35 hierarchy inspection confirming the image contributes no accessibility + label or species token; and +- a black Android ADB capture confirming `FLAG_SECURE` remains active. Capture protection + was not weakened to obtain a background screenshot. + +These are local engineering checks. Exact-SHA CI, physical-device review, assistive- +technology review, patient-advisor review, language/RTL review, signed distribution +artifacts, and store presentation remain open. + +## 6. Distribution and change-control gates + +No catalog asset is approved for external distribution until all of the following are +recorded: + +1. durable source archive location; +2. license, permission, and attribution obligation for each source; +3. release-owner approval of the exact manifest version and hashes; +4. named patient-advisor and accessibility review of comfort, legibility, crop, cultural + interpretation, and images-hidden behavior; +5. supported physical-device and orientation inspection; +6. signed iOS and Android distribution-artifact verification; +7. store-listing and privacy-declaration review where applicable; and +8. exact-SHA CI plus the ordinary protected release gates. + +A future addition, replacement, recompression, crop, or deletion increments the catalog +version and requires the complete lineage, native parity, artifact, accessibility, +rights, and human-review sequence. Silent file replacement is prohibited. + +## 7. Safety statement + +No production database or source was accessed. No patient, principal, encounter, grant, +session, identity link, clinical projection, message, preference, feature flag, migration, +deployment, pilot, or release state was created, read, or changed. diff --git a/docs/nightingale/BACKGROUND-RIGHTS-AND-SOURCE-ARCHIVE-REVIEW-2026-07-27.md b/docs/nightingale/BACKGROUND-RIGHTS-AND-SOURCE-ARCHIVE-REVIEW-2026-07-27.md new file mode 100644 index 00000000..6409ce61 --- /dev/null +++ b/docs/nightingale/BACKGROUND-RIGHTS-AND-SOURCE-ARCHIVE-REVIEW-2026-07-27.md @@ -0,0 +1,178 @@ +# Nightingale background rights and source-archive review + +**Date:** 2026-07-27 + +**State:** Evidence inventory complete; external distribution remains on hold + +**Scope:** Seven decorative Nightingale background sources and their existing catalog +lineage + +**Legal determination:** None + +**Distribution-eligible assets:** 0 of 7 + +## 1. Outcome + +This review converts the prior single prose hold into an exact, per-asset, machine-checked +rights and archive queue. It does not clear any image for pilot, production, App Store, +Play Store, marketing, or other external distribution. + +The repository now proves all of the following: + +- the review covers the exact seven catalog entries, in order; +- every review row is bound to the catalog source filename, dimensions, and SHA-256; +- two provider asset pages are identified from the provider asset IDs embedded in the + supplied filenames; +- one current provider download is byte-for-byte identical to the catalog source; +- the other identified provider asset is strongly corroborated as the same image after + resize/re-encoding, but its exact catalog source binary is not available from the + provider at the checked download URL; +- five exact provider or purchase sources remain unresolved; +- no organization-controlled original archive, durable provider/purchase record, durable + license snapshot, or release-owner approval is recorded for any image; and +- no code or document can promote the v0 review to an approved distribution state without + failing CI. + +The machine-readable source of this result is the +[v0 rights review](../../nightingale/backgrounds/rights/rights-review.v0.json). The +independent verifier is +[`verify-nightingale-background-rights.mjs`](../../scripts/ci/verify-nightingale-background-rights.mjs). + +## 2. Evidence standard + +The review uses a deliberately narrow standard: + +1. A filename is lineage, not proof of a provider, author, purchase, or license. +2. A photographer profile is not proof that a specific image came from that profile. +3. A live provider page identifies an asset only when its asset ID, creator, dimensions, + composition, and supplied-file lineage reconcile. +4. A live license page is a current observation, not a durable record of the terms that + applied when the file was obtained. +5. A developer workstation path is not an organization-controlled archive. +6. A committed optimized derivative is not the original source archive. +7. A binary match proves identity to the checked download; it does not itself establish + the applicable rights, preserve the terms, or authorize a release. +8. Automated checks can prove record completeness and prevent overstatement. They cannot + issue a legal determination or appoint the release owner. + +## 3. Catalog reconciliation + +| Catalog ID | Supplied source filename | Provider/source result | Binary reconciliation | Durable archive | Release eligible | +| --------------------------- | ------------------------------------------------------------------------------------- | ------------------------------------------------------------ | --------------------------------------------------------------------------------------------------------------------------- | --------------- | ---------------- | +| `nightingale-background-01` | `1031776-1400x1980-phone-hd-robin-bird-background-photo.jpg` | exact provider or purchase source unresolved | catalog hash retained only | no | no | +| `nightingale-background-02` | `HD-wallpaper-nightingale-bird-feathers-bright.jpg` | exact provider or purchase source unresolved | catalog hash retained only | no | no | +| `nightingale-background-03` | `beautiful-sparrow-sitting-thin-branch-tree.jpg` | exact provider or purchase source unresolved | catalog hash retained only | no | no | +| `nightingale-background-04` | `common-linnet-bird-carduelis-cannabina-perched-branch-tree-with-yellow-blossoms.jpg` | exact provider or purchase source unresolved | catalog hash retained only | no | no | +| `nightingale-background-05` | `miguel-alcantara-VCq1vozVMbw-unsplash.jpg` | Unsplash asset `VCq1vozVMbw`, Miguel Alcântara, corroborated | provider currently serves 2608x3912; supplied 2400x3600 file has normalized RMSE `0.0112669` and pHash distance `0.0247332` | no | no | +| `nightingale-background-06` | `muhammad-shakir-g3mOCylYkmk-unsplash.jpg` | Unsplash asset `g3mOCylYkmk`, Muhammad Shakir, exact | current 3456x5184 download SHA-256 equals catalog source SHA-256 `507f7325…37f1` | no | no | +| `nightingale-background-07` | `vertical-closeup-shot-brown-shrike-bird-perched-branch.jpg` | exact provider or purchase source unresolved | catalog hash retained only | no | no | + +The two identified provider pages are: + +- [Miguel Alcântara, Unsplash asset `VCq1vozVMbw`](https://unsplash.com/photos/a-small-bird-perched-on-a-tree-branch-VCq1vozVMbw) +- [Muhammad Shakir, Unsplash asset `g3mOCylYkmk`](https://unsplash.com/photos/a-small-bird-sitting-on-top-of-a-brick-wall-g3mOCylYkmk) + +On 2026-07-27, both pages labeled the images free to use under the +[Unsplash License](https://unsplash.com/license). The corresponding +[Unsplash terms](https://unsplash.com/terms) distinguish the image license from depicted +trademarks, recognizable people, and works of authorship. These bird images do not visibly +introduce those three depicted-subject categories, but that observation is not a legal +clearance or a substitute for the required archived terms and release-owner review. + +## 4. Why the two identified assets remain held + +Assets 05 and 06 are materially closer to closure than the other five, but neither meets +the release gate: + +- the exact catalog source binary is not stored in an organization-controlled, + checksum-verifiable archive; +- the source page and applicable license/terms have not been captured into a durable, + versioned rights record; +- no dated release-owner or legal review is recorded; and +- the v0 record intentionally cannot express approval. + +Asset 05 has an additional limitation: the current provider download is a larger source +encoding, while the supplied catalog source is a 2400x3600 re-encoding. The image +comparison corroborates identity, but a release archive must retain the exact catalog +source SHA-256 rather than reconstructing it later. + +## 5. Required durable archive + +For each of the seven assets, the release owner must create a record with: + +| Required field | Acceptance rule | +| ------------------------- | ------------------------------------------------------------------------------------------------------------------- | +| Original binary location | organization-controlled, access-controlled, retrievable without one person’s workstation, and governed by retention | +| Original binary checksum | retrieved bytes must equal the catalog `source.sha256` | +| Source or purchase record | exact provider asset/purchase locator and acquisition evidence, not a search result or profile | +| Creator/rightsholder | named source-backed creator or other rightsholder | +| Applicable terms | durable snapshot of the license, purchase terms, or written permission that applies to that exact asset | +| Terms identity | capture timestamp or version plus SHA-256 of the archived record | +| Attribution disposition | exact required credit, or an explicit source-backed statement that attribution is not required | +| Release-owner decision | named reviewer, date, decision, scope, exceptions, and successor review record | + +An archive is acceptable only when it is organization-controlled, access-controlled, +versioned or object-locked, checksum-verifiable, retention-governed, and independent of a +single workstation. Repository paths to optimized derivatives and local absolute paths +are explicitly rejected as release evidence. + +## 6. Fail-closed mechanical control + +Run: + +```bash +node scripts/ci/verify-nightingale-background-assets.mjs . --self-test +node scripts/ci/verify-nightingale-background-rights.mjs . --self-test +``` + +The rights verifier: + +- hashes the exact catalog file and checks the digest recorded by the review; +- reconciles every review row to catalog ID, filename, source dimensions, and source hash; +- requires exactly five unresolved source rows and exactly two identified Unsplash rows; +- requires the exact observed creator, asset ID, canonical page, publication timestamp, + dimensions, current download hash, and reconciliation method for those two rows; +- keeps all seven archive locations, archive checks, approvals, and eligibility facts + empty or false; +- requires the global legal determination to be `none`, distribution status to be `hold`, + and automated release permission to be false; +- verifies summary counts of 7 total, 2 identified pages, 1 byte-for-byte current download + match, 5 unresolved sources, 0 durable archives, 0 rights-cleared assets, and 0 + distribution-eligible assets; and +- executes 20 adversarial mutations covering invented approval, release eligibility, + archive claims, provider guesses, source/hash drift, terms claims, summary inflation, + filename-only evidence, and catalog-digest drift. + +The verifier is called by both the Nightingale contract CI job and the native product +boundary chain. It performs no network request: volatile provider observations are stored +as review evidence, while a future lift requires a deliberately reviewed successor record +and verifier revision. + +## 7. Closure procedure + +1. Resolve exact provider or purchase sources for assets 01, 02, 03, 04, and 07. +2. Place all seven exact originals in the approved durable archive. +3. Retrieve each archived object and verify its SHA-256 against the catalog. +4. Archive exact source/purchase evidence and applicable terms with their own checksums. +5. Record creator/rightsholder and attribution requirements for every image. +6. Obtain a named release-owner or legal decision for each image and the seven-image set. +7. Create a versioned successor review; do not edit v0 into an approval. +8. Update the catalog distribution state and verifier only in the same reviewed change. +9. Run the full Nightingale contract, native artifact, iOS Simulator, and Android emulator + acceptance matrix before any external distribution. + +Until all nine steps pass, the plan’s parent rights/archive checkbox remains open and the +seven images remain foundation-only assets. + +## 8. Residual risks + +- Five sources may prove to require paid licenses, attribution, replacement, or removal. +- Provider terms can change; the live observations in this record are not durable terms + snapshots. +- Asset 05’s current provider encoding is not the catalog source binary. +- No provider warranties, model/property releases, or indemnities have been evaluated. +- The rights record does not replace the separate named patient-advisor and accessibility + review of image comfort, crop, legibility, cultural interpretation, or images-hidden + behavior. +- No signed artifact, store submission, pilot, production deployment, or marketing use is + authorized by this work. diff --git a/docs/nightingale/COMMUNICATION-AND-NOTIFICATION-SOURCE-CLASSIFICATION-2026-07-26.md b/docs/nightingale/COMMUNICATION-AND-NOTIFICATION-SOURCE-CLASSIFICATION-2026-07-26.md new file mode 100644 index 00000000..380b6110 --- /dev/null +++ b/docs/nightingale/COMMUNICATION-AND-NOTIFICATION-SOURCE-CLASSIFICATION-2026-07-26.md @@ -0,0 +1,650 @@ +# Nightingale communication and notification source classification + +**Date:** 2026-07-26 +**Status:** complete evidence classification for this bounded slice; not approved for +implementation, runtime adoption, patient disclosure, mutation, notification delivery, +production data use, or release +**Reviewed source commit:** `b6ea087747d7ea88c8a076f06f4c91a2636ea029` +**Last source revalidation:** 2026-07-27 +**Last source revalidation commit:** `a979e78630c214f6f77d96ce702227da6c03a9b6` +**Machine-readable ledger:** +[`migration/candidates/v0/communication-notification-source-classification.json`](./migration/candidates/v0/communication-notification-source-classification.json) +**Verifier:** +[`scripts/ci/verify-nightingale-communication-notification-classification.mjs`](../../scripts/ci/verify-nightingale-communication-notification-classification.mjs) + +## 1. Executive conclusion + +The Hummingbird Patient communication foundation contains several safety properties worth +reimplementing, but it is not functionally safe or semantically complete enough to become +Nightingale by renaming, routing, copying, or enabling it. + +The 130-file review establishes five material conclusions: + +1. **The server-side accountable-message core is the strongest part of the legacy + implementation.** It revalidates the current patient relationship, uses immutable + encrypted message bodies, content-free routing facts, append-only receipts and events, + optimistic concurrency, transaction locks, replay digests, staff responsibility pools, + effective responder membership, and fresh consumer heartbeats. +2. **Patient notification delivery does not exist end to end.** The backend can encrypt, + register, rotate, rebind, and revoke an APNs/FCM provider token, but registration is not + delivery. There is no patient provider worker, payload policy, delivery lifecycle, + patient-native registration, iOS remote-notification capability, or Android patient + notification service/permission flow. +3. **A human retry after an ambiguous network result is not safely idempotent in either + patient client.** The backend can replay an operation only when the same operation and + client-message identifiers are reused. iOS and Android generate fresh identifiers for a + new user retry, so an accepted request whose response was lost can be duplicated. +4. **Patient-visible delivery wording exceeds the evidence recorded by the server.** The + first receipt is `server_accepted` with visible state `sent`, followed by an asynchronous + `staff_inbox` outbox record. That proves durable server acceptance, not staff projection, + individual receipt, or care-team review. Both native clients use wording that implies + delivery to the care team or responsibility pool. +5. **The cross-platform state contract has one decode-breaking iOS gap and incomplete + Android rendering.** Escalation writes patient-visible delivery state `escalated`, while + the contract and iOS delivery enum omit it. Android accepts strings but fails to map all + valid ownership and delivery states to precise patient language. + +This classification does not authorize a Nightingale route, provider, payload, message, +topic, urgent-help string, staff workflow, poller, queue, migration, source query, patient, +feature flag, or deployment. Nightingale remains a separate, zero-operation, +network-disabled product foundation. + +## 2. Scope and inventory + +### 2.1 Required domains + +Every source is classified against one or more of these six domains: + +| Domain | Classified sources | Question answered | +| ------------------------------------ | -----------------: | ----------------------------------------------------------------------------------------------------- | +| Patient communication contract | 77 | What paths, schemas, state values, copy, permissions, and response claims exist? | +| Patient mutation and delivery | 103 | How are create, reply, amend, close, replay, receipt, and delivery facts governed? | +| Staff handoff and routing | 89 | How does accepted patient input become accountable staff work, and who can act on it? | +| Notification registration/delivery | 69 | What exists for device registration, provider delivery, payloads, lifecycle, consent, and revocation? | +| Native patient experience | 48 | How do iOS and Android compose, retry, refresh, render states, explain urgency, and fail offline? | +| Error, offline, and urgency behavior | 130 | What can be inferred, overstated, queued, lost, duplicated, or exposed when dependencies fail? | + +### 2.2 Reviewed surfaces + +| Surface | Sources | +| ---------------------------- | ------: | +| Legacy backend | 65 | +| Legacy database | 5 | +| Legacy contract/reference | 3 | +| Legacy backend tests | 9 | +| Legacy patient iOS | 6 | +| Legacy patient iOS tests | 6 | +| Legacy patient Android | 8 | +| Legacy patient Android tests | 5 | +| Legacy staff iOS | 8 | +| Legacy staff Android | 7 | +| Legacy staff web | 8 | +| **Total** | **130** | + +The exact path inventory has SHA-256 digest +`ca199936c1d6dc58fb9ba21accbd7376a1d72512f6b1adc42d8f9719672306a0`. +Every file also has its own SHA-256 digest in the machine-readable ledger. Removal, +replacement, duplication, or content drift fails CI. + +### 2.3 Base-refresh revalidation + +The feature stream was synchronized with `origin/main` at +`b6ea087747d7ea88c8a076f06f4c91a2636ea029` after the original classification. All 130 +classified paths were mechanically compared with that exact commit and match it. Exactly +one source digest changed: `routes/api.php`, from +`a1443123cbee888a55f988aa1ca713fe31eac39fa6cf05a0306eef0def3aad35` to +`052d9afd466b2646385986297e9ce3ec6a886d299b35c76067f5a0f21d7fa082`. + +The twelve added lines register three Zephyrus staff/operations reads: scoped Arena +per-case conformance, an aggregate patient-flow epoch, and a scoped patient-flow journey. +They do not add, alias, enable, or modify a Nightingale route, patient communication +operation, notification provider, patient-native transport, or communication delivery +state. The broad `routes/api.php` evidence source therefore remains classified under +`routes_rejected`; no implementation permission, finding, disposition, or activation gate +changes. This is a source revalidation of the same v1 classification, not a new functional +approval. + +The feature stream was reconciled again with `origin/main` at +`a979e78630c214f6f77d96ce702227da6c03a9b6`. The exact 130-path inventory remained +unchanged, and `routes/api.php` was again the only classified source whose bytes changed: +its SHA-256 moved from +`052d9afd466b2646385986297e9ce3ec6a886d299b35c76067f5a0f21d7fa082` to +`a357886ae0975b63d978b975e53518a97d3f6e907820255a0f1c9e1b76a986ad`. + +The 15-addition/five-deletion delta places the existing staff Arena conformance read +behind a separate Flow 4D conformance gate and adds a staff-only scene read plus a +governed exception-note proposal. Every operation remains inside the staff Arena route +group and its scoped patient-flow authorization. The delta does not add a Nightingale +route, patient communication operation, notification provider, patient-native transport, +delivery-state claim, or migration permission. `routes/api.php` therefore remains +`routes_rejected`, every activation constraint remains false, and no finding or +disposition changes. The machine-readable ledger now records this second bounded +revalidation explicitly instead of allowing a current byte digest to float away from its +review record. + +### 2.4 Dispositions + +| Disposition | Sources | Meaning | +| -------------------------- | ------: | ----------------------------------------------------------------------------------------------------------- | +| Evidence only | 55 | Retain behavior and tests as compatibility/hazard evidence; they cannot authorize Nightingale behavior. | +| Reimplement principle only | 27 | A bounded safety property is valuable, but no code, identifier, schema, copy, state, or policy is reusable. | +| Held | 42 | Product, clinical, privacy, security, content, accessibility, notification, or operations decisions remain. | +| Reject | 6 | Legacy routes and configuration ownership are incompatible with the independent Nightingale boundary. | + +There is no `approved`, `migrate`, `copy`, `enable`, or `production-ready` disposition. + +## 3. Review method and evidence standard + +The review traced each patient mutation in both directions: + +1. patient-visible composer and urgent-help presentation; +2. native request construction and retry behavior; +3. API contract, route, feature gates, scopes, policy, and request validation; +4. current encounter/grant and thread authorization; +5. transaction, lock, replay, encryption, receipt, routing event, and audit; +6. content-free staff inbox outbox; +7. handoff consumer, responsibility pool, responder eligibility, and heartbeat; +8. staff web/iOS/Android read and mutation surfaces; +9. acknowledgement, response, reassignment, reroute, escalation, closure, and + patient-visible serialization; +10. failure, stale state, outage, offline, notification, and post-discharge behavior. + +Claims are classified using this evidence hierarchy: + +| Evidence level | What it can prove | +| ------------------- | ---------------------------------------------------------------------------------------------------- | +| Schema/constraint | Allowed persisted values, immutability, uniqueness, relationships, and structural invariants | +| Service/transaction | Runtime checks, lock order, replay behavior, encryption use, emitted events, and state transitions | +| Contract/route | Publicly described request/response vocabulary and declared authorization boundary | +| Native/web client | Actual request identity, refresh, wording, state rendering, and local failure behavior | +| Test | A checked example or hazard regression; not production readiness, clinical approval, or completeness | +| Strategy document | Intent only; not an implemented provider, payload, permission, queue, or lifecycle | + +No production database, runtime secret, patient record, provider console, or deployed +environment was required or accessed for this classification. + +## 4. End-to-end behavior found + +### 4.1 Patient message submission + +The legacy mutation service: + +- derives an operation digest from the authenticated principal, operation name, and + idempotency key; +- derives a payload digest from the client message UUID, message-body HMAC, thread, + optimistic version, and urgent-guidance version; +- revalidates ownership before the transaction and again under a row lock; +- obtains advisory locks for operation and client-message identities; +- detects replay by either operation digest or client-message UUID and rejects + cross-payload reuse; +- requires an approved mutation policy, matching urgent-guidance version, current + disclosable encounter, routable responsibility pool, open thread, and matching version; +- encrypts the body with contextual associated data; +- appends a `server_accepted` receipt with patient-visible state `sent`; +- appends a content-free routing event; +- appends a `staff_inbox` outbox fact; +- records disclosure/mutation audit after serialization succeeds. + +These are strong candidate principles. They do not resolve the client retry gap or make +`server_accepted` equivalent to staff delivery. + +### 4.2 Staff handoff + +The database handoff consumer: + +- selects content-free `staff_inbox` outbox records; +- projects accountable staff work without placing message content in the transport record; +- resolves the configured responsibility pool; +- requires an effective eligible responder and a fresh consumer heartbeat before patient + compose can be considered routable; +- records append-only delivery attempts and staff actions; +- supports claim, reply, close, release, reassignment, reroute, lifecycle reconciliation, + and response-window escalation. + +Staff web, iOS, and Android surfaces poll while foregrounded, generally every 20 seconds. +This is evidence that the staff side anticipates changing responsibility state. It does not +provide a patient-side update mechanism. + +### 4.3 Patient readback + +Patient thread serialization returns the latest receipt’s raw +`patient_visible_state`. Consequently, the public contract and every patient client must +accept every state that the database and services can emit. The current implementation does +not meet that invariant for `escalated`. + +## 5. Confirmed safety properties worth reimplementing + +The following properties may inform a new Nightingale design after named approvals. Their +legacy implementations are not approved for reuse. + +### 5.1 Authorization and non-disclosure + +- Product, operation, token ability, principal relationship, encounter/grant, resource + ownership, mutation policy, and staff readiness are separate gates. +- Thread UUID possession is not authorization. +- The encounter/grant is revalidated at mutation time and under lock. +- Ordinary ownership failures collapse to non-disclosing not-found behavior. +- Cross-principal notification-device access collapses to not found. +- Response protection and audit are centralized. + +### 5.2 Content protection and audit + +- Message bodies are application-encrypted and context-bound. +- Body HMACs support replay comparison without placing plaintext in routing metadata. +- Staff outbox, routing, delivery attempt, heartbeat, and audit facts are content free. +- Corrections and retractions append; the source message is never rewritten or deleted. +- Delivery receipts and routing events are append-only ledgers. +- Device provider tokens are encrypted and never returned by the API. + +### 5.3 Concurrency and replay + +- Operation and client-message identities are independently checked. +- Advisory locks serialize both replay identities. +- Payload digests bind identity to semantic content. +- Thread and staff work-item versions prevent blind overwrites. +- Transactional revalidation prevents an authorization check from becoming stale before + persistence. + +### 5.4 Responsibility and lifecycle + +- Topics resolve to explicit responsibility pools. +- A pool is not routable merely because it exists; membership, capability, effective + window, and consumer health are checked. +- Claim and responder actions are attributable. +- Escalation and reconciliation are explicit events rather than silent state changes. +- Patient-visible receipts are structurally separate from staff operational events. + +## 6. Blocking gaps and incompatibilities + +### 6.1 Patient push delivery is absent + +The `notification_devices` feature registers an encrypted and revocable provider token. +The configuration itself states that this does not enable a provider, payload delivery, or +push. The reviewed source contains no patient APNs/FCM sender, payload builder, localization +release, collapse/deduplication policy, priority policy, expiry, delivery worker, retry +budget, dead-letter policy, or provider receipt reconciliation. + +The patient native clients also do not complete the lifecycle: + +| Requirement | iOS patient | Android patient | Server | +| ----------------------------------------------- | :---------: | :-------------: | :------------------: | +| Permission rationale and request | No | No | N/A | +| Obtain provider token | No | No | N/A | +| Register/rotate/revoke through patient API | No | No | Registry only | +| Logout/account-switch revocation evidence | No | No | Endpoint only | +| APNs/FCM provider integration | N/A | N/A | No | +| Approved generic payload policy | No | No | No | +| Deep-link authorization after notification | No | No | No | +| Delivery attempt and provider receipt lifecycle | No | No | Schema concepts only | +| Actual-notification accessibility/privacy QA | No | No | No | + +Nightingale must not expose “App notification,” email, or SMS delivery preferences until +the corresponding governed channel actually exists. A preference is not consent, and a +registered provider token is not a delivered notification. + +### 6.2 Patient automatic refresh is absent + +The patient clients support manual refresh/retry. They do not poll foreground messaging and +they cannot receive patient push. Therefore an acknowledgement, response, reroute, +escalation, or closure can remain invisible until the patient manually refreshes. + +Nightingale must choose and approve a freshness model before implementation: + +- foreground polling with bounded cadence and backoff; +- server-driven push that carries no clinical content and only prompts an authorized + refresh; +- an authorized streaming mechanism; +- or an explicit manual-only policy with patient-facing freshness language. + +No option is selected by this record. “Copy the staff 20-second poller” is not an approved +decision because patient battery, network, accessibility, support, and privacy conditions +differ from the staff application. + +### 6.3 Ambiguous retries can duplicate patient mutations + +The backend replay contract is sound only when the client preserves operation identity. +The current native behavior is: + +| Stage | iOS patient | Android patient | Consequence | +| ------------------------------------------- | ---------------------------------------------------------- | ------------------------------------------------------------------- | ----------------------------------------------------- | +| First compose attempt | Generates new idempotency key and client-message UUID | Generates new values inside coordinator call | Server can replay this exact request | +| Automatic token refresh inside same request | Closure/request object retains values | Request object retains values | Same-call authentication replay is bounded | +| Response lost after server commit | Draft remains visible, operation identity is not persisted | Draft may remain in volatile UI, operation identity is not retained | Client cannot know whether commit occurred | +| Patient taps retry | Generates new operation and message identities | Generates new identities | Server sees a new mutation and may create a duplicate | + +An offline mutation queue is **not** approved as the remedy. The minimum future design must +define a volatile pending-operation record that binds: + +- action and target; +- normalized payload digest; +- client message UUID; +- idempotency key; +- expected thread/work-item version; +- creation and expiry timestamps; +- outcome `not_started`, `in_flight`, `unknown`, `accepted`, `rejected`, or `superseded`; +- explicit rules for retry, refresh-first reconciliation, discard, logout, account switch, + backgrounding, process death, and sensitive-memory clearing. + +Whether that record may survive process death is a security and product decision. Until +approved, Nightingale has neither an offline queue nor durable pending-message storage. + +### 6.4 “Sent to your care team” overstates delivery + +On initial acceptance, the server writes: + +1. immutable patient message; +2. receipt type `server_accepted`, visible state `sent`; +3. routing event `message_submitted`, state `awaiting_team`; +4. asynchronous outbox destination `staff_inbox`. + +At that moment the server has not proved that: + +- the handoff consumer projected the work; +- a staff client fetched it; +- an eligible responder claimed it; +- any named team member saw it; +- a response will occur within the displayed window. + +The iOS success string “Your message was sent to your care team” and Android strings that +say it was sent to the responsible care-team pool exceed this evidence. A future +Nightingale vocabulary should distinguish at least: + +| Evidence fact | Candidate patient meaning | Prohibited inference | +| -------------------------------- | ---------------------------------------- | --------------------------------------------------- | +| Server transaction committed | “Saved securely” or approved equivalent | A team member received/read it | +| Staff inbox projection delivered | “Delivered to the care-team inbox” | A responder claimed/read it | +| Responsibility assigned | “Assigned to the responsible team” | A specific person read it | +| Staff acknowledgement | “Care team acknowledged it” | A clinical response is complete | +| Staff patient-visible response | “Care team responded” | Patient has read/understood response | +| SLA escalation | Approved transparent escalation language | Emergency handling or guaranteed immediate response | + +Patient-language, clinical operations, legal/privacy, accessibility, localization, and +support owners must approve the final words and evidence-to-label map. + +### 6.5 Escalated delivery state can break iOS decoding + +The database and escalation service allow and emit: + +- thread ownership state `escalated`; +- routing event patient-visible state `escalated`; +- latest patient-message receipt type and visible state `escalated`. + +The patient API serializer returns the latest receipt state without normalization. The +OpenAPI delivery-state enum and iOS `PatientMessageDeliveryState` omit `escalated`. A +strict iOS decode of an escalated thread can therefore fail the entire thread response. + +This is not merely a display-label issue. Nightingale must enforce a generated or otherwise +mechanically reconciled state registry across: + +- schema constraints; +- backend transition emitters; +- serializer; +- contract; +- fixtures; +- iOS decoding and labels; +- Android decoding and labels; +- accessibility text; +- analytics/telemetry; +- support documentation. + +Unknown future states must follow an explicitly approved compatibility rule. Silently +mapping an unknown state to “sent” or failing the whole conversation are both unsafe +defaults. + +### 6.6 Android state rendering is incomplete + +The backend/contract ownership states are: +`awaiting_team`, `assigned`, `acknowledged`, `responded`, `rerouted`, `escalated`, and +`closed`. + +Android checks `team_acknowledged`, which is not the contract value, and does not provide +precise patient labels for all emitted ownership values. Its delivery rendering also lacks +specific handling for valid `assigned`, `responded`, `escalated`, and `closed` states. +Permissive string decoding prevents a hard parse failure but produces ambiguous patient +language. + +### 6.7 Urgent guidance is not a governed locale-bound release + +The server correctly requires the client to echo an urgent-guidance version and rejects a +stale version. That is a strong anti-stale principle. The content itself is still a static +configuration value, and topic labels/descriptions are code-owned English strings. They are +not bound to: + +- product release; +- facility/unit/cohort; +- locale and reading level; +- clinical/content approval; +- accessibility review; +- interpreter and communication-support workflow; +- effective period and rollback; +- checksum; +- support and downtime instructions. + +iOS additionally shows fixed local urgent/offline wording. Android relies more heavily on +the server-provided urgent text and lacks the same always-visible no-offline-queue +explanation. The products therefore do not have proven safety-copy parity. + +### 6.8 Error and absence states are under-specified + +Patient messaging load generally interprets a 404-like result as disabled/unavailable. +Nightingale must decide which states may be distinguished without creating an oracle: + +- operation disabled; +- access not granted; +- grant revoked; +- encounter no longer current; +- source unavailable; +- staff route not ready; +- policy/version unavailable; +- temporary network failure; +- server accepted but outcome unknown; +- stale thread version; +- message amended/closed by another actor; +- post-discharge retention boundary. + +The response must remain non-disclosing, but collapsing every state into the same UI can +also cause unsafe retry or false reassurance. The contract needs an approved safe-error +registry plus exact client transitions and accessibility announcements. + +### 6.9 Notification registration input is less strict than messaging input + +Messaging request objects explicitly reject unknown JSON properties and control characters. +The notification-device registration request does not apply the same unknown-property +rejection. This does not prove an exploit, but it is an unnecessary contract asymmetry. +Nightingale should make exact JSON shape, size, canonicalization, platform/provider +allowlists, and unknown-field behavior explicit before any device endpoint exists. + +### 6.10 Staff close reasons do not break patient decoding + +This suspected hazard was tested against the service path and is explicitly recorded as +**not present**. The staff work item retains staff-specific reasons such as operational +reason codes, while the patient thread is normalized to `question_answered`. The patient +serializer therefore returns a value accepted by the patient contract and iOS enum. + +This negative finding is pinned so a future review does not repeat or incorrectly report +the concern. It also establishes a useful rule: operational reason detail and +patient-visible reason vocabulary may differ, but their mapping must be explicit, +versioned, and tested. + +## 7. Cross-platform semantic reconciliation + +### 7.1 Ownership states + +| Backend/contract state | Backend emits | iOS decodes | Android decodes | Android precise label | Nightingale status | +| ---------------------- | :-----------: | :---------: | :-------------: | :-----------------------------------------: | ------------------------------------- | +| `awaiting_team` | Yes | Yes | Yes | Partial | Held; approve evidence-based language | +| `assigned` | Yes | Yes | Yes | Yes | Held | +| `acknowledged` | Yes | Yes | Yes | No; checks non-contract `team_acknowledged` | Reject current mapping | +| `responded` | Yes | Yes | Yes | Generic | Held | +| `rerouted` | Yes | Yes | Yes | Generic | Held | +| `escalated` | Yes | Yes | Yes | Generic | High-risk held | +| `closed` | Yes | Yes | Yes | Generic | Held | + +### 7.2 Delivery states + +| Persisted/emitted state | Contract accepts | iOS accepts | Android precise label | Finding | +| ----------------------- | :--------------: | :---------: | :-------------------: | ------------------------------------------------ | +| `sent` | Yes | Yes | Yes | Means server accepted; copy currently overstates | +| `delivered` | Yes | Yes | Yes | Evidence definition needs approval | +| `assigned` | Yes | Yes | No | Android parity gap | +| `acknowledged` | Yes | Yes | Yes | Evidence-to-language mapping held | +| `responded` | Yes | Yes | No | Android parity gap | +| `escalated` | No | No | No | Contract/iOS decode-breaking gap | +| `closed` | Yes | Yes | No | Android parity gap | + +### 7.3 Refresh and delivery mechanisms + +| Capability | Patient iOS | Patient Android | Staff iOS | Staff Android | Staff web | +| ----------------------------------- | :---------: | :-------------: | :-------------------------------: | :----------------------: | :----------------------: | +| Manual refresh | Yes | Yes | Yes | Yes | Yes | +| Foreground polling | No | No | 20 s evidence | 20 s evidence | 20 s evidence | +| Patient push | No | No | N/A | N/A | N/A | +| Offline mutation queue | No | No | Not Nightingale evidence | Not Nightingale evidence | Not Nightingale evidence | +| Retry identity survives human retry | No | No | Not assessed for patient contract | Not assessed | Not assessed | + +## 8. Nightingale implementation gates + +This section is an implementation plan, not approval to implement. + +### Gate C1 — Product and clinical communication model + +- [ ] Name the Nightingale communication product owner. +- [ ] Name clinical operations owners for every facility/unit/cohort. +- [ ] Define eligible topics and explicitly prohibited uses. +- [ ] Define responsibility pool, coverage hours, response window, escalation, downtime, + and no-responder behavior per topic. +- [ ] Define emergency/urgent escape routes and prove they are continuously reachable. +- [ ] Define representative/minor/sensitive-service and post-discharge communication rules. +- [ ] Approve patient-visible evidence-to-state language. +- [ ] Approve correction, retraction, closure, retention, legal hold, and audit semantics. + +### Gate C2 — Contract and state registry + +- [ ] Create Nightingale-owned operations in a candidate contract; do not copy paths or + operation IDs from Hummingbird. +- [ ] Define one version/checksum-bound registry for topics, ownership states, delivery + states, close reasons, safe errors, and urgent content. +- [ ] Generate or mechanically reconcile backend, iOS, Android, fixtures, and documentation. +- [ ] Add exhaustive transition and unknown-state tests. +- [ ] Define response freshness and provenance without claiming source freshness from + request time. +- [ ] Prove IDOR, grant revocation, encounter transfer, stale source, stale version, and + non-disclosure cases. + +### Gate C3 — Mutation identity and reconciliation + +- [ ] Define operation identity creation, retention, expiry, and clearing. +- [ ] Preserve the same operation and client-message identities after an ambiguous result. +- [ ] Refresh/reconcile before creating a new identity. +- [ ] Prove process death, logout, account switch, backgrounding, auth refresh, network + timeout, server 5xx, response truncation, and duplicate-tap cases. +- [ ] Do not create an offline queue unless separately approved. +- [ ] Provide patient-visible `unknown outcome` handling that does not encourage duplicate + resubmission. + +### Gate C4 — Accountable staff handoff + +- [ ] Specify pool resolution from authoritative encounter/facility/unit context. +- [ ] Specify responder eligibility, capabilities, effective windows, and delegation. +- [ ] Define heartbeat, queue health, lag, retry, poison-message, and dead-letter policy. +- [ ] Prove compose fails closed before any patient text is accepted when the staff route is + not ready. +- [ ] Prove accepted input becomes one accountable work item or is transparently reconciled. +- [ ] Prove reassignment, reroute, escalation, closure, discharge, transfer, and downtime. +- [ ] Keep message content out of queue, routing, metrics, logs, and notification payloads. + +### Gate N1 — Notification channel decision + +- [ ] Decide whether Nightingale needs push, email, SMS, none, or a phased subset. +- [ ] Separate presentation preferences from communication consent and channel enrollment. +- [ ] Approve provider projects/accounts, credentials, rotation, environment separation, + and least privilege. +- [ ] Define installation/device identity without reusing Hummingbird identifiers. +- [ ] Define registration, rotation, rebind, logout, account switch, uninstall, expiry, + provider invalidation, and deletion evidence. +- [ ] Define generic payload, localization, collapse, deduplication, TTL, priority, quiet + hours, and sensitive-state suppression. +- [ ] Require notification taps to reauthenticate/re-authorize and fetch current data; never + trust payload content as patient data. + +### Gate N2 — Native notification implementation + +- [ ] Add iOS capability/entitlement only after provider approval. +- [ ] Add Android runtime permission and service only after provider approval. +- [ ] Implement registration/revocation against Nightingale-owned endpoints. +- [ ] Test denied, provisional, restricted, disabled, token-rotation, and provider-failure + states. +- [ ] Test lock-screen preview, accessibility, localization, account switch, app reinstall, + backup/restore, and device transfer. +- [ ] Prove no clinical detail, patient identity, room, diagnosis, medication, test, or + message text appears in payload, logs, analytics, or provider console. + +### Gate C5 — Patient freshness and offline experience + +- [ ] Select and document patient refresh mechanism and cadence. +- [ ] Define background/foreground transitions and exponential backoff. +- [ ] Expose last successful refresh without mislabeling it as clinical source freshness. +- [ ] Distinguish unsent, in flight, outcome unknown, securely accepted, staff-inbox + delivered, assigned, acknowledged, responded, escalated, and closed. +- [ ] Keep unsent text volatile by default; define clearing for logout, account switch, + backgrounding, recovery, and memory pressure. +- [ ] Complete equivalent iOS and Android accessibility announcements and recovery actions. + +### Gate C6 — Verification and release + +- [ ] Contract-schema validation and state-registry drift checks pass. +- [ ] Backend authorization, replay, encryption, routing, lifecycle, and negative tests pass. +- [ ] iOS unit, UI, accessibility, Debug, Release, notification, offline, and upgrade tests + pass on supported simulators/devices. +- [ ] Android unit, instrumentation, accessibility, Debug, Release, notification, offline, + and upgrade tests pass on supported emulators/devices. +- [ ] Production-like queue lag, worker failure, EHR/source outage, provider outage, + rollback, and kill-switch exercises pass. +- [ ] Named clinical, privacy, security, content, accessibility, identity, operations, + support, and release owners sign the exact artifact checksum. +- [ ] Only then add a default-off non-production implementation behind approved ports. +- [ ] Production activation remains a separate protected-main release and pilot decision. + +## 9. CI enforcement + +The verifier pins: + +- exact 130-source inventory, source hashes, six domain counts, 11 surface counts, four + disposition counts, and 17 decision records; +- the ten material positive and negative findings; +- every runtime, route, copy, provider, device, channel, payload, polling, offline, + retry-regeneration, delivery-overstatement, production-data, production-query, + production-replay, and patient-creation permission to `false`; +- the zero-operation Nightingale contract and dormant backend configuration; +- absence of credential-like literals in the ledger. + +Its negative self-test proves rejection of: + +- notification provider activation; +- patient foreground polling activation; +- offline queue activation; +- retry identity regeneration; +- server acceptance treated as care-team delivery; +- production replay; +- false resolution of the iOS escalation mismatch; +- false assertion of a staff-close decode failure; +- invalid hashes; +- source removal; +- duplicate inventory paths; +- an `approved` disposition. + +## 10. Completion boundary + +This bounded communication and notification source-classification section is complete when: + +- the 130-file ledger validates against the reviewed source commit; +- the negative verifier passes; +- the Nightingale contract remains empty and default off; +- the master plan, migration index, contract matrix, documentation index, and devlog link + this record; +- existing Nightingale iOS and Android emulator suites still pass without gaining network, + messaging, notification, patient-data, or production capabilities. + +Completion of this section does **not** mean communication or notifications are implemented. +It means the predecessor behavior, reusable principles, incompatibilities, and approval +gates are now explicit and mechanically protected. diff --git a/docs/nightingale/CONTRACT-OWNERSHIP-AND-AUTHORIZATION-MATRIX-2026-07-26.md b/docs/nightingale/CONTRACT-OWNERSHIP-AND-AUTHORIZATION-MATRIX-2026-07-26.md new file mode 100644 index 00000000..f83c8da3 --- /dev/null +++ b/docs/nightingale/CONTRACT-OWNERSHIP-AND-AUTHORIZATION-MATRIX-2026-07-26.md @@ -0,0 +1,410 @@ +# Nightingale contract ownership and authorization matrix + +**Status:** Governance foundation with a reserved Nightingale route namespace; no API +operation, registered route, usable server, security scheme, client, identity flow, source +query, patient disclosure, mutation, feature flag, pilot, or production use is approved by +this document. + +**Decision date:** 2026-07-26 + +**Machine-verifiable companion:** +[`api-contract/nightingale-foundation.v0.json`](./api-contract/nightingale-foundation.v0.json) + +**Held prerequisite companions:** +[identity candidate](./identity/candidates/v0/candidate.json), +[current-inpatient source candidate](./source-candidates/current-inpatient/v0/candidate.json), +their +[decision record](./IDENTITY-SESSION-RECOVERY-AND-SOURCE-CANDIDATE-DECISION-2026-07-26.md), +the held +[patient-journey reference catalog](./PATIENT-JOURNEY-REFERENCE-SCENARIO-CANDIDATE-DECISION-2026-07-27.md), +and the +[complete 256-source migration classification](./JOURNEY-PREFERENCE-PRESENTATION-RELEASE-SOURCE-CLASSIFICATION-2026-07-26.md) + +## 1. Outcome + +Nightingale now owns a contract-development boundary without pretending that it owns an +active API. The first OpenAPI artifact is intentionally empty: + +- zero paths and zero operations; +- no usable server; +- no security scheme or credential format; +- no generated client permission; +- one reserved product namespace with no registered route; +- every activation category explicitly false; and +- a verifier that fails if an operation or legacy/staff route enters the artifact. + +This makes ownership concrete while keeping runtime behavior unchanged. It also prevents +the existing Hummingbird Patient contract from becoming the Nightingale contract through a +filename change, route alias, or generated-client shortcut. + +## 2. Evidence snapshot and lineage + +The source review was performed against the following repository snapshot after merging +`origin/main` commit `84b5f830`: + +| Evidence | SHA-256 | Observed scope | Nightingale use | +| ----------------------------------------------------------- | ------------------------------------------------------------------ | ---------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------- | +| `docs/hummingbird/api-contract/hummingbird-patient.v1.yaml` | `fb6220b4ef8eb106223624a9785256fdc1603995f281f430a79897905cb45a1b` | OpenAPI 3.1.0; 23 paths; 25 operations; draft/not approved | Compatibility and hazard input only | +| `routes/patient.php` | `e892a9d570668bec6ae54d18cbfd891f7d9df2e2c2f8f6e3689125d6b465af28` | Separate `/api/patient/v1` route group with product/feature/realm/rate gates | Current backend evidence; no Nightingale route approval | +| `config/hummingbird-patient.php` | `d5dafe054077892e649fd44f95edc6bc1bb499e4dffeb4f4390316823db07625` | Default-off legacy flags, draft policy, token/session choices, projection and messaging policy | Control inventory only; legacy names and identity choices are not inherited | + +Relevant enforcement was also traced through: + +- `EnsureHummingbirdPatientEnabled`, `EnsureHummingbirdPatientFeatureEnabled`, and + `EnsurePatientRealm`; +- `PatientEncounterAccessGrantPolicy`, `PatientEncounterProjectionPolicy`, and + `PatientMessageThreadPolicy`; +- `PatientProjectionDisclosureService`, `PatientProjectionContentGuard`, and + `PatientResponseDecorator`; +- `PatientCommunicationEncounterGuard`, `PatientMessagingPolicyRegistry`, and + `PatientMessagingService`; and +- the patient principal, session, grant, projection, notification-device, and communication + models. + +The inventory is source-level evidence, not a claim that the current backend is +Nightingale-approved or production-active. No database or deployed configuration was read. + +## 3. Ownership and approval responsibilities + +Ownership means responsibility for a versioned artifact and its evidence. It does not let +one owner self-approve another discipline's gate. + +| Concern | Accountable role | Required evidence before an operation can enter a candidate contract | +| ----------------------------------- | ----------------------------------------------------- | --------------------------------------------------------------------------------------------------------- | +| Product purpose and patient journey | Nightingale product owner | Patient problem, non-goals, user state, failure/recovery journey, support path | +| Contract and compatibility | Patient Experience Platform contract owner | Operation schema, compatibility classification, deprecation/rollback, generated-fixture diff | +| Backend boundary | Zephyrus Patient Experience Boundary owner | Route/middleware/service mapping, default-off configuration, authorization and audit tests | +| Identity and representatives | Identity/security owner with privacy and legal/HIM | Proofing, patient/proxy relationship, enrollment, recovery, session, revocation, enumeration controls | +| Clinical projection/content | Named clinical safety and content-release authorities | Field provenance, release state, freshness, uncertainty, correction/retraction, patient-language approval | +| Communication workflow | Nursing/medical operations plus safety owner | Topic, responsible pool, responder eligibility, service hours, handoff, escalation, downtime, retention | +| Privacy and security | Privacy and security owners | Data minimization, threat model, storage/log/push controls, penetration findings, residual risks | +| Accessibility and language | Accessibility and language/interpreter owners | Native semantics, large text, assistive journeys, translation lifecycle, accommodation handling | +| Support and incident response | Support/operations owner | Lost/shared device, lockout, patient escalation, outage, kill switch, incident and recovery runbooks | +| Release | Independent release authority | Exact artifact/SHA, CI evidence, rollback, scope/expiry-bound pilot manifest, signed go/no-go | + +No named individuals or delegates are recorded yet, so every operation remains held. + +## 4. Version and namespace decisions + +### 4.1 Foundation version + +`0.0.0-governance` is not an API version. It is a machine-readable assertion that there is +no runnable Nightingale API. Client generation and route registration are prohibited. + +### 4.2 Reserved API namespace + +The +[route, compatibility, identity, and inpatient-source ADR](./ROUTE-COMPATIBILITY-IDENTITY-SOURCE-ADR-2026-07-26.md) +reserves `/api/nightingale/v1`. The first held relative path is +`/inpatient-contexts`, with operation ID `listNightingaleInpatientContexts`. + +The decision rejects aliases, proxies, redirects, second-prefix mounting, and native fallback +to `/api/patient/v1` or `/api/mobile/v1`. Shared internal services remain possible only +behind Nightingale-owned request/response, authorization, audit, and release adapters. + +Reservation is not route registration. The foundation retains zero OpenAPI paths, a +non-routable `.invalid` server, no security scheme, no generated client, no Nightingale route +file or controller, and every activation field false. Endpoint discovery, +certificate/pinning posture if applicable, deep-link boundaries, rollout, dual-running, +telemetry separation, deprecation, and rollback remain operation/release decisions. + +### 4.3 Versioning rules + +- Adding the first operation changes the contract out of foundation status and requires + every pre-operation gate in the JSON artifact. +- A breaking schema, identifier, authorization, failure, idempotency, release, freshness, + correction, or audit change requires a new major API version or a documented + compatibility window. +- Additive fields must remain safe for older clients. A client must withhold content whose + state-vocabulary or schema version it does not understand. +- Server and client support windows, minimum versions, forced-upgrade behavior, and + emergency revocation are release decisions, not implicit semantic-version behavior. +- Generated sources never become authoritative. The reviewed OpenAPI artifact and + authorization evidence remain authoritative. + +## 5. Authorization lattice + +Every future request must satisfy all applicable layers. Passing one layer never bypasses +another. + +```text +operation exists in approved Nightingale contract + -> Nightingale product and operation release gates are effective + -> ingress/media/rate/size policy accepts the request + -> identity realm, principal, session, and credential are valid + -> patient or representative relationship is currently effective + -> encounter grant belongs to that principal and permits the exact scope + -> resource belongs to the same grant and relationship + -> disclosure release OR mutation capability gates are satisfied + -> audit succeeds at the required assurance + -> patient-safe no-store response +``` + +### 5.1 Contract and release gate + +- The operation must exist in the current approved Nightingale contract. +- Product, facility, unit/cohort, operation, content, and communication flags are all + default-off and independently revocable. +- Disabled or unauthorized resource operations use a non-disclosing response. A client + configuration value cannot authorize an operation the server contract does not contain. + +### 5.2 Ingress gate + +- HTTPS, supported media type, explicit body-size limit, schema validation, and + operation-specific rate limiting are mandatory. +- Request IDs are non-secret and cannot be patient/source identifiers. +- Unknown fields fail validation on mutations unless an explicit forward-compatibility + policy says otherwise. + +### 5.3 Identity/session gate + +- Only the approved Nightingale patient/representative realm is accepted; staff users and + staff token abilities are categorically rejected. +- Principal status, lock/closure state, session status, absolute expiry, idle expiry, + revocation, credential family, assurance level, and risk policy are independently + evaluated. +- No access/refresh credential format is approved. The legacy Sanctum token-name/ability + convention is evidence only. +- Pre-proofing failures must not reveal whether an email, phone, medical record, encounter, + representative relationship, or account exists. +- The held identity/session/recovery candidate pins 64 synthetic outcomes and self-only + positive-state prerequisites. It supplies no provider, credential, route, binding, or + authorization, and all representative relationships remain denied. + +### 5.4 Relationship and encounter-grant gate + +At minimum, a grant decision evaluates: + +- exact principal ownership; +- verified patient identity link; +- relationship (`self` or a separately approved representative type); +- purpose of use; +- active status, start, expiry, and revocation; +- opaque Nightingale encounter handle; +- facility/unit/cohort/pilot policy; +- exact operation scope; and +- current grant version. + +Unknown relationships, purposes, statuses, scopes, or versions fail closed. The existing +reference projections currently permit `self`; that is not approval for a representative +model. A representative must never inherit all patient fields or communication powers by +default. + +### 5.5 Resource-ownership gate + +A resource UUID is only a lookup hint. The server must bind the resource to the already +authorized principal, grant, encounter, relationship, and operation. Querying by UUID and +checking later is unsafe if error, timing, audit, or relationship behavior can disclose +existence. + +Wrong-principal, wrong-relationship, wrong-grant, wrong-encounter, revoked, expired, +unknown, withdrawn, and omitted resources collapse to the same non-disclosing outcome +unless a reviewed patient-safety need requires a different response after identity is +already established. + +### 5.6 Released-projection gate + +Patient-visible projections additionally require: + +- an effective, approved disclosure-policy version; +- exact projection kind and required scope; +- a relationship allowlist; +- released state and effective release time; +- content-schema and patient-state-vocabulary versions; +- approved field allowlist and content guard; +- source/provenance and observed/generated timestamps; +- freshness class, uncertainty, correction/supersession, and retraction actions; +- no active withdrawal/content action; and +- an auditable disclosure decision. + +Draft, review, raw FHIR/EHR, internal note, internal source identifier, inferred risk, +operational score, and unsupported vocabulary are never serialized. + +### 5.7 Communication/mutation gate + +Communication requires all read gates plus: + +- `messaging:read` and `messaging:write` (or future operation-specific scopes); +- a currently active/routable encounter for a new write; +- approved nonurgent topic and urgent-help copy/version; +- effective facility/unit responsibility pool; +- eligible responder and healthy accountable consumer; +- service-hour, escalation, handoff, downtime, retention, correction, and closure policy; +- server-side encryption and redacted logging/audit; +- an idempotency key bound to principal, operation, and canonical payload; +- optimistic concurrency/version checks where state can change; and +- deterministic success, replay, conflict, and ambiguous-network behavior. + +The mobile client must never maintain an opaque offline mutation outbox or claim delivery +from a transport acknowledgement alone. + +### 5.8 Audit and response gate + +- Required audit failure is fail-closed for disclosures/mutations whose evidence must be + durable; best-effort denial audit cannot turn a denial into an allow. +- Secrets, free text, raw payloads, source identifiers, and push tokens never enter audit + metadata or logs. +- Successes and errors use `private, no-store, max-age=0`, no indexing, and a stable + non-diagnostic envelope. +- Response metadata includes request identity, generated time, disclosure-policy version, + state-vocabulary version, freshness, source-observed time where approved, version, and + correction/retraction signals. + +## 6. Scope and relationship matrix + +These are candidate minimums derived from current source controls. They are not final +Nightingale scopes. + +| Capability | Candidate minimum scope/authority | Relationship rule | Additional mandatory control | +| ---------------------------------- | ------------------------------------------------------------- | -------------------------------------------------------------------- | --------------------------------------------------------------------------------------- | +| Profile | Valid principal/session | Field-level patient versus representative projection | Minimize email/phone/display-name disclosure; non-disclosing identity errors | +| Accessibility/language preferences | Valid principal/session; server persistence only if necessary | Actor may change only their own experience settings | Prefer local settings; split communication consent/channel from display preferences | +| Encounter list | Grant view | Return only grants effective for exact principal/relationship | Opaque handles only; no source encounter IDs | +| Today | `today:read` | Projection relationship allowlist | Released projection, freshness, uncertainty, correction/retraction | +| My Path/events/discharge/rounds | `pathway:read` | Projection relationship allowlist, potentially field-specific | Clinical/content release; no promises or raw notes | +| Care Team | `care_team:read` | Projection relationship allowlist | Released roles/contact capabilities only; no schedules or personal contacts | +| Conversation list/thread | `messaging:read` | Same grant and relationship as thread | Approved policy, non-disclosure, active disclosure rules | +| New/reply/amend/close | `messaging:read` + `messaging:write` | Same grant/relationship; field/action-specific representative policy | Active routable encounter, accountable pool, idempotency, concurrency | +| Education clarification | `pathway:read` + messaging read/write | Item and projection must be visible to the same relationship | Opaque education item belongs to current released pathway | +| Session management | Approved identity/session authority | A representative manages only their own sessions | Non-disclosing ownership; idempotent revoke; local versus remote outcome separation | +| Notification registration | Approved push/device authority | Binding belongs to the authenticated actor, not the viewed patient | Separate provider identity, encryption, generic payload, reinstall/rebind/revoke policy | + +## 7. Legacy operation-by-operation disposition + +`Candidate` means only that the patient need remains in the Nightingale charter. Every row +is held from implementation and activation. + +| # | Legacy operation | Current reference controls observed | Nightingale disposition and missing gates | +| --: | ------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| 1 | `POST /auth/enroll/challenge/verify` (`verifyPatientEnrollmentChallenge`) | Product/feature flag, enrollment throttle, challenge/grant verification, audit | **Hold/re-design.** Identity provider, assurance, delivery, enumeration, representative enrollment, recovery, and support are undecided. | +| 2 | `POST /auth/token` (`exchangePatientCredentials`) | Product/feature flag, credential throttle, patient principal/password, session/token creation | **Hold/re-design.** No password or credential-exchange design is approved. | +| 3 | `POST /auth/token/refresh` (`refreshPatientSession`) | Patient realm, refresh ability/name, active session, rotation/reuse handling | **Hold/re-design.** Nightingale has intentionally not approved a refresh credential. | +| 4 | `POST /auth/token/revoke` (`revokePatientSession`) | Patient realm, access-or-refresh ability, session revocation, audit | **Candidate after identity.** Must define idempotent remote outcome and local/remote ambiguity. | +| 5 | `GET /me` (`getPatientProfile`) | Active patient session, disclosure audit, patient profile serialization | **Candidate/minimize.** Patient versus representative fields and contact-data necessity require review. | +| 6 | `PUT /me/preferences` (`updatePatientPreferences`) | Active session, allowlisted locale/timezone/display/notification fields, audit | **Candidate/split.** Local accessibility settings should not require server storage; channel/preview settings require consent and notification policy. | +| 7 | `PUT /me/notification-devices/{deviceUuid}` (`registerPatientNotificationDevice`) | Feature flag, principal ownership, encrypted token, HMAC digest, rebind/revoke transaction | **Hold.** No Nightingale push project, installation identity, payload, consent, provider, or support policy exists. | +| 8 | `DELETE /me/notification-devices/{deviceUuid}` (`revokePatientNotificationDevice`) | Principal ownership, idempotent revocation state, audit | **Hold with push.** Must define uninstall/logout/account-switch and provider-deletion evidence. | +| 9 | `GET /me/sessions` (`listPatientSessions`) | Active access session, principal-owned sessions, safe device projection | **Candidate after identity.** Device descriptors, shared-device risk, and representative ownership are undecided. | +| 10 | `DELETE /me/sessions/{sessionUuid}` (`revokePatientSessionById`) | Principal-scoped UUID, current-session context, idempotent outcome, audit | **Candidate after identity.** Must not revoke another actor's session or overclaim local cleanup. | +| 11 | `GET /encounters` (`listPatientEncounterAccess`) | Principal-owned effective grants, policy, opaque grant/encounter UUIDs, audit | **Held first-read candidate.** The [candidate decision](./ENCOUNTER-ACCESS-CANDIDATE-DECISION-2026-07-26.md) removes grant internals and defines 42 synthetic outcomes, but does not satisfy the approval gates or add an operation. | +| 12 | `GET /encounters/{encounterUuid}/today` (`getPatientTodayProjection`) | `today:read`, grant policy, effective release policy, released projection, relationship allowlist, content actions | **Replaced as a contract-design input only by the held [Nightingale Today candidate](./TODAY-PROJECTION-CANDIDATE-DECISION-2026-07-26.md).** The new candidate removes legacy identifiers and aggregate context, defines field-level governance and 68 synthetic outcomes, and still adds no operation. Identity/source and every clinical/content/language approval remain open. | +| 13 | `GET /encounters/{encounterUuid}/pathway` (`getPatientPathwayProjection`) | `pathway:read` plus projection release/content controls | **Core read candidate.** Only clinically released, governed pathway content; no draft catalog or predicted promise. | +| 14 | `GET /encounters/{encounterUuid}/pathway/events` (`getPatientPathwayEvents`) | `pathway:read` plus append-only/released history controls | **Candidate after pathway.** Event chronology, source reconciliation, corrections, and understandable state vocabulary require approval. | +| 15 | `GET /encounters/{encounterUuid}/discharge-readiness` (`getPatientDischargeReadiness`) | `pathway:read` plus released projection/content controls | **Candidate/high scrutiny.** Timing uncertainty, criteria, medications, warning signs, and discharge changes need explicit clinical/HIM rules. | +| 16 | `GET /encounters/{encounterUuid}/rounds/summary` (`getPatientRoundsSummary`) | `pathway:read` plus released summary and separation from staff rounds | **Candidate.** Patient-language release and post-round correction rules are required. | +| 17 | `GET /encounters/{encounterUuid}/care-team` (`getPatientCareTeamProjection`) | `care_team:read` plus released projection/content controls | **Core read candidate.** Only approved roles and contact capabilities; no schedule, internal role, or personal contact leakage. | +| 18 | `GET /encounters/{encounterUuid}/message-topics` (`listPatientMessageTopics`) | Messaging feature/policy, grant scopes, active encounter and governed routing | **Hold.** Topics, urgent guidance, pools, responders, service windows, language, and downtime need approval. | +| 19 | `GET /encounters/{encounterUuid}/threads` (`listPatientMessageThreads`) | `messaging:read`, principal/grant policy, active/disclosable encounter, no-store response | **Hold with communication.** Retention, post-discharge visibility, proxy access, corrections, and retractions are unresolved. | +| 20 | `POST /encounters/{encounterUuid}/threads` (`createPatientMessageThread`) | Read/write scopes, active/routable encounter, policy/topic/pool, idempotency, encryption, audit | **Hold/high risk.** Accountable routing and deterministic delivery evidence must be proven end to end. | +| 21 | `POST /encounters/{encounterUuid}/education/{educationItemUuid}/clarifications` (`requestPatientEducationClarification`) | Pathway + messaging scopes, item in current released projection, policy/routing/idempotency | **Hold.** Clarification must never record comprehension, completion, consent, or clinical assessment. | +| 22 | `GET /threads/{threadUuid}` (`getPatientMessageThread`) | Principal owns thread grant, `messaging:read`, non-disclosing not-found | **Hold with communication.** UUID alone never authorizes; representative/post-discharge rules remain open. | +| 23 | `POST /threads/{threadUuid}/messages` (`sendPatientMessage`) | Read/write, thread policy, active route, thread version, urgent-guidance version, idempotency, audit | **Hold/high risk.** Network ambiguity, consumer health, delivery states, escalation, and support require proof. | +| 24 | `POST /threads/{threadUuid}/messages/{messageUuid}/amend` (`amendPatientMessage`) | Same thread/grant, sender/action rules, concurrency, idempotency, append-only correction/retraction | **Hold/high risk.** Patient-visible history, retention, notification, and staff reconciliation need approval. | +| 25 | `POST /threads/{threadUuid}/close` (`closePatientMessageThread`) | Same thread/grant, close reason allowlist, concurrency, idempotency, routing transition | **Hold/high risk.** Closure authority, reopening/new-thread behavior, handoff, and unresolved-work warnings need approval. | + +## 8. Candidate response contract invariants + +Before a read operation can be specified, its fixture matrix must prove: + +- an envelope with `data`, `meta`, and `links`; +- exact opaque-handle formats with no database keys or source linkage; +- `policy_version`, `state_vocabulary_version`, generated/as-of time, data version, and + operation-specific freshness/source-observed time; +- explicit no-data, stale, source-unavailable, content-withheld, corrected, superseded, and + retracted states; +- field-level provenance retained server-side and only patient-safe source language exposed; +- deterministic locale/translation fallback without inventing a clinical explanation; +- unknown enum/vocabulary behavior that withholds rather than guesses; and +- identical non-disclosure behavior for unknown, wrong-principal, wrong-relationship, + wrong-encounter, revoked, expired, omitted, and withdrawn resources. + +Before a mutation can be specified, its fixture matrix must additionally prove: + +- canonical request digest and idempotency scope; +- replay of identical content versus conflict on different content; +- optimistic concurrency and current urgent-guidance/policy version; +- exact audit event and redacted fields; +- server acceptance, routing, delivery, handoff, and patient-visible state definitions; +- ambiguous timeout/retry behavior with no false success; +- correction/retraction and retention behavior; and +- logout/revocation preventing replay. + +## 9. Required evidence before the first operation + +- [ ] Named owners and independent approvers for every applicable row in section 3. +- [x] ADR for route namespace and compatibility/deprecation behavior, mechanically pinned + with route registration still prohibited. +- [x] Non-runnable identity/session/recovery and current-inpatient-source candidate state + designs, exact synthetic fixture matrices, and negative verifier mutations, with every + provider/adapter/route/query/client/activation field still held. +- [x] Source-by-source evidence classification for the bounded identity-input, + enrollment/recovery, first-read, and error/non-disclosure slice: 65 exact files, + SHA-256 lineage, platform delta, missing-recovery finding, and negative enforcement of + zero runtime/route/provider/production adoption. +- [x] Source-by-source evidence classification for the bounded communication and + notification slice: 130 exact files, SHA-256 lineage, patient-to-staff delivery trace, + native parity delta, ten pinned findings, and negative enforcement of zero + runtime/route/provider/channel/payload/polling/offline-queue/production adoption. +- [x] Source-by-source evidence classification for the remaining journey, preference, + accessibility-presentation, synthetic/debug, persistence, and release slice: 134 exact + files, SHA-256 lineage, full 256-source universe closure, native context/preference/ + motion/navigation deltas, rejected deployed reference provisioning, and negative + enforcement of zero implementation/runtime/source/mutation/production/deployment + adoption. +- [x] Held, non-runnable Today projection candidate: Nightingale-owned opaque handles, + explicit empty-versus-unavailable section semantics, field-level release/freshness/ + uncertainty/language/correction/offline context, bounded correction/retraction and + generic non-disclosure behavior, 68 synthetic cases, 14 direct-source checksums, and + 24 adversarial verifier mutations, with the executable contract still at zero paths. +- [x] Held, non-runnable cross-surface patient-journey reference catalog: 15 required + admission-through-retraction families, 27 synthetic cases, 12 exact evidence-source + checksums, and 23 adversarial mutations. It keeps every operation, representative, + clinical release, communication, notification, offline PHI, source, database, + production, and deployment permission false. +- [x] Route-free generic non-disclosure foundation: exact relationship, context-binding, + resource, and public-disposition vocabularies; one identifier-free 404 tuple for all + unknown, revoked, expired, cross-principal, wrong-encounter, omitted-resource, and + failed-precondition cases; and an independently repeated 40-row truth table with one + non-authorizing continue row and 39 withholds. +- [x] Route-free activation-separation foundation: five distinct clinical-approval, + patient-content-release, feature-activation, pilot-enrollment, and source-connector + state types; code-owned negative defaults; five additional false executable-contract + fields; and an independently repeated 32-row truth table with 31 holds and one + non-authorizing continue row. +- [x] Exact current native namespace foundation: deterministic 12-source manifest, ten + shared accessibility identifiers, four Debug hooks, ten fully qualified persistent + identifiers, zero telemetry events, zero diagnostic channels, no legacy product + namespace, and eight negative verifier mutations. +- [x] Default-off controlled-pilot configuration candidate: exact facility/unit/cohort/ + language/exclusion/support-hour/validity/approval/audit/rollback shape; empty + committed template; seven-day and 25-enrollment technical ceilings; 34 synthetic + cases with 33 holds and one external-review-only result; and 25 negative mutations. + No manifest repository, evaluator, enrollment service, route, deployment, or + activation exists. +- [ ] Approved Nightingale identity, representative, enrollment, recovery, and session + contract. +- [ ] Approved authoritative current-inpatient source, lifecycle, cohort, linkage, + freshness, outage, audit, and adapter contract. +- [ ] Operation-specific authorization and non-disclosure matrix with automated tests. +- [ ] Patient-visible field/source/release/freshness/uncertainty/correction/translation + matrix. +- [ ] Error/envelope/cache/rate/body-size/audit contract and fixtures. +- [ ] Mobile generated-client boundary and binary scan with network still default-off. +- [ ] Threat model and privacy/security review. +- [ ] Patient-advisor, accessibility, language, clinical/content, legal/HIM, support, and + operations review. +- [ ] Default-off non-production integration plan and rollback. + +Until these gates are met, `paths` remains empty. The controlled-pilot candidate defines +the future scope record but does not satisfy any named approval, integration, release, or +go/no-go gate. + +The encounter-access, Today, patient-journey reference, identity/source, and three +source-classification ledgers are pre-contract evidence for these gates, not proof that an +approval gate is complete. The route/compatibility ADR, default-deny prerequisite ports, +the previously recorded 174 identity/source/encounter/Today cases, 40 route-free generic +non-disclosure permutations, 32 separately gated activation permutations, 27 additional +cross-surface journey cases, exact current native namespace, complete 256-source inventory, +and detailed native/backend deltas exist, but +no owner is named, identity provider/source adapter is approved, clinical field matrix is +released, or backend/native operation parity implementation exists. diff --git a/docs/nightingale/CONTROLLED-PILOT-MANIFEST-FOUNDATION-2026-07-27.md b/docs/nightingale/CONTROLLED-PILOT-MANIFEST-FOUNDATION-2026-07-27.md new file mode 100644 index 00000000..fc3cc25f --- /dev/null +++ b/docs/nightingale/CONTROLLED-PILOT-MANIFEST-FOUNDATION-2026-07-27.md @@ -0,0 +1,467 @@ +# Nightingale controlled-pilot manifest foundation + +**Date:** 2026-07-27 + +**Status:** generated, default-off governance candidate; not approved, implemented, or +authorized for pilot use + +**Product:** Nightingale + +**Executable API paths:** 0 + +**Runtime evaluator/callers:** 0/0 + +**Current production effect:** none + +**Acceptance evidence:** +[`controlled-pilot-manifest-2026-07-27`](../evidence/nightingale/controlled-pilot-manifest-2026-07-27/README.md) + +## 1. Executive disposition + +Nightingale now has a mechanically defined configuration boundary for the first controlled +pilot. It describes the exact facility, unit, cohort, language, exclusion, support-hour, +validity, prerequisite, named-approval, audit, rollback, and kill-switch fields that a +future pilot manifest must contain. + +The committed manifest is deliberately empty and inactive: + +- no facility, unit, cohort, or locale is selected; +- maximum active enrollment is zero; +- no support window is claimed; +- start and expiry are absent; +- no approval, clinical release, source deployment, audit sink, rollback plan, or + kill-switch test is recorded; +- go/no-go review has not been requested; and +- runtime activation remains prohibited. + +The candidate therefore cannot enroll anyone or make any Nightingale behavior available. +It closes only the plan requirement to define a fail-closed manifest shape. It does not +close identity, content, accessibility, privacy/security, clinical-safety, integration, +distribution, pilot-authorization, deployment, or production gates. + +## 2. Why this boundary is implemented before live functionality + +The current Nightingale foundation has five independent activation states, but it +previously had no concrete pilot-scope record. An `enrolled` state without an exact, +expiry-bounded scope would be ambiguous about: + +- which facility and units are covered; +- which cohort and exclusion policy apply; +- which languages and interpreter coverage are supported; +- when staffed support is available; +- how many participants can be active; +- when the authorization ends; +- which approvals, releases, source deployment, and audit sink are prerequisites; +- which kill switch and rollback target apply; and +- which durable events prove that a scope changed or expired. + +The manifest candidate eliminates that ambiguity without enabling a runtime. It is +designed so that missing, malformed, stale, contradictory, or unavailable inputs hold +before external go/no-go review. + +## 3. Generated artifacts and authoritative implementation + +| Artifact | Role | +| ----------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------- | +| [`pilot/candidates/v0/candidate.json`](./pilot/candidates/v0/candidate.json) | Generated candidate policy, empty committed template, constraints, technical ceilings, approval/audit inventories, and checksum-bound source evidence | +| [`pilot/candidates/v0/fixtures.json`](./pilot/candidates/v0/fixtures.json) | Synthetic no-PHI complete example and 34 deterministic evaluation cases | +| [`build-nightingale-controlled-pilot-manifest.mjs`](../../scripts/ci/build-nightingale-controlled-pilot-manifest.mjs) | Deterministic artifact builder | +| [`verify-nightingale-controlled-pilot-manifest.mjs`](../../scripts/ci/verify-nightingale-controlled-pilot-manifest.mjs) | Independent structural, semantic, source-hash, foundation-state, fixture, and negative-mutation verifier | + +The builder pins four current inputs: + +1. code-owned Nightingale backend configuration; +2. the zero-path executable contract; +3. the five-input activation gate; and +4. the pilot-enrollment state type. + +The verifier reruns the builder and requires exact JSON equality. A hand edit cannot +silently change a scope, ceiling, audit inventory, fixture outcome, source digest, or +non-authorization constraint. + +## 4. Candidate and runtime separation + +The candidate recognizes only two dispositions: + +| Disposition | Meaning | Explicitly does not mean | +| -------------------------------------------- | --------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------- | +| `hold` | The manifest is incomplete, malformed, outside its validity window, or inconsistent with a fail-closed pilot boundary | that a person or resource exists; that any one approval is missing; or that retrying will succeed | +| `eligible_for_external_go_no_go_review_only` | The synthetic structure contains every required record and may be presented to independent governance owners | activation, enrollment, deployment, disclosure, source authorization, clinical release, distribution, or production approval | + +The candidate has no runtime evaluator. The JavaScript evaluator exists only in +repository verification and is not imported by Laravel, iOS, or Android. No route, +controller, service provider, native client, database model, feature flag, environment +variable, or production query consumes either JSON artifact. + +The following candidate constraints are all fixed to `false`: + +- runtime implementation; +- route registration; +- native networking; +- identity-provider selection; +- source-adapter selection; +- production query; +- patient or representative creation; +- patient enrollment; +- patient disclosure; +- patient mutation; +- communication or notification; +- deployment; and +- pilot activation. + +## 5. Manifest field semantics + +### 5.1 Identity and revision + +Every future manifest must have: + +- a Nightingale-owned `manifest_id`; +- a positive, monotonically advancing `manifest_revision`; +- the exact candidate `policy_version`; +- an `approved_inactive` state before external review; and +- an explicit request for external go/no-go review. + +No `active` manifest state exists in this candidate. Even a structurally complete record +must retain `runtime_activation_permitted=false`. + +The committed template uses revision zero and `draft_inactive`. It is not a placeholder +that defaults into an active scope. + +### 5.2 Facility and unit scope + +Facility and unit values use opaque Nightingale handles: + +- facility handles use the `ngf_` prefix; and +- unit handles use the `ngu_` prefix. + +The candidate does not accept source-system identifiers, hospital names, unit names, +database keys, FHIR references, MRNs, or other embedded source meaning. At least one +facility and one unit are required for external review. Empty, duplicate, or malformed +lists hold. + +The committed template contains no handles. The complete fixture uses synthetic handles +only and is not a proposed production scope. + +### 5.3 Cohort configuration + +The cohort block requires: + +- one opaque `ngc_` cohort handle; +- separately versioned inclusion and exclusion policy release IDs; +- the same exclusion release in both the cohort and exclusion blocks; +- a positive maximum active-enrollment count no greater than 25; +- `enrollment_is_automatic=false`; and +- `unknown_or_unavailable_eligibility_result=withhold`. + +The 25-person ceiling is a conservative technical maximum for this candidate, not a +recommended clinical cohort size. A real pilot may set a smaller number, and independent +governance owners must choose the actual number. Raising the technical ceiling requires a +reviewed candidate version, fixture changes, and exact-SHA ratification. + +No algorithm is permitted to infer enrollment from presence in a unit, encounter status, +diagnosis, pathway, language, or use of the sample patient. + +### 5.4 Language and interpreter configuration + +The language block requires: + +- at least one unique canonical locale tag; +- a released interpreter-coverage record; +- no unapproved locale fallback; and +- `withhold` for an unknown language. + +The verifier performs a bounded canonical tag-shape check consistent with BCP 47-style +language, optional script, and optional region structure. That mechanical check is not a +translation approval, locale registry, language-access assessment, or interpreter-service +validation. + +The complete fixture uses `en-US` and `es-US` only as synthetic examples. The committed +template approves no language. A future manifest cannot use the presence of English copy +in the offline shell as evidence that English, Spanish, or any other language is approved +for a pilot. + +### 5.5 Exclusion behavior and non-disclosure + +The exclusion block requires: + +- a released exclusion-policy identifier that matches the cohort block; +- denial when a rule result is unknown; +- denial when a rule source is unavailable; +- no inference that a sensitive service exists; and +- no disclosure of the internal exclusion reason. + +This foundation defines the fail-closed shape but no clinical exclusion rules. It does not +select diagnoses, services, ages, legal statuses, accommodations, behavioral-health +conditions, reproductive-health conditions, interpreter needs, or other sensitive facts. +Those decisions require named clinical, legal/HIM, privacy, patient-advisor, and +accessibility review. + +### 5.6 Support hours + +Support coverage requires: + +- an IANA-style timezone identifier; +- at least one non-overlapping weekly local-time window; +- an approved urgent-help content release; +- a fixed uncovered-window disposition of + `withhold_new_enrollment_and_show_released_support_guidance`; and +- no patient-visible inference about staffing availability. + +Weekly windows use ISO weekday values 1 through 7 and exact `HH:MM` local start/end times. +Cross-midnight windows are intentionally not accepted in this candidate; they must be +split across weekdays to make review and overlap detection explicit. + +The candidate validates window shape and overlap only. It does not implement daylight +saving transitions, holiday exceptions, emergency staffing, service-level targets, +contact routes, or support content. A future approved implementation must define and test +those behaviors before enrollment. + +### 5.7 Validity and expiry + +An inactive committed template may have null validity because it cannot proceed to +external review. Any candidate eligible for external review must have: + +- exact UTC `starts_at` and `expires_at` timestamps; +- `starts_at < expires_at`; +- an evaluation time within `[starts_at, expires_at)`; +- a duration no greater than 168 hours; +- `renewal_requires_new_manifest=true`; and +- `expiry_fails_closed=true`. + +The seven-day ceiling is a technical authorization lifetime, not a statement about patient +length of stay or pilot duration. A longer pilot would require a new manifest and new +external authorization before the current manifest expires. Silent extension, reuse, or +automatic renewal is prohibited. + +An expired manifest holds. It does not disclose whether the prior scope, cohort, person, +or approval existed. + +### 5.8 Prerequisite records + +All eight prerequisite references must exist before external review: + +1. institutional clinical approval; +2. patient-content release; +3. independent feature activation; +4. source-connector deployment; +5. identity/source approval; +6. durable audit-sink deployment; +7. rollback plan; and +8. kill-switch test. + +A reference is only a linkage requirement. The candidate does not validate the substance, +signatures, currentness, scope, or authority of a future record. Those checks require an +independently reviewed implementation and governance repository. + +### 5.9 Named approval roles + +Seven distinct roles must each provide a named subject, approval record, and UTC approval +timestamp before the manifest validity begins: + +1. product owner; +2. clinical-safety owner; +3. privacy/security owner; +4. patient-advisor/accessibility owner; +5. identity/source owner; +6. operations/support owner; and +7. release owner. + +The committed template contains no names or approvals. The fixture values are explicitly +synthetic. This document neither appoints approvers nor records their decisions. + +One person filling multiple future roles is not approved by this candidate. Independence, +delegation, conflicts, revocation, quorum, signature, and evidence-retention rules remain +governance decisions. + +### 5.10 Audit requirements + +The manifest requires an append-only sink and an event schema that are durable before an +effective change. Nine event types are mandatory: + +1. manifest created; +2. review requested; +3. manifest approved; +4. go/no-go requested; +5. pilot started; +6. pilot expired; +7. pilot withdrawn; +8. kill switch invoked; and +9. rollback completed. + +Every event must contain: + +- manifest ID and revision; +- event type and UTC occurrence time; +- actor role and a hashed actor reference; +- outcome and a bounded reason code; and +- policy version. + +Patient identifiers, clinical content, and message content are prohibited in this +candidate's audit events. Audit failure holds. + +This is an audit contract candidate, not an audit implementation. There is no writer, +sink, retention schedule, key-management design, actor-hash construction, access policy, +alert, reconciliation job, or evidence export. Those must be reviewed before the audit +prerequisite can be considered real. + +### 5.11 Rollback and kill switch + +The kill switch defaults to `engaged`. External review requires: + +- a rollback target release; +- a rollback verification record; +- a separately referenced kill-switch test; and +- prohibition of enrollment and disclosure after expiry. + +The candidate does not implement a kill-switch service or deploy a rollback target. The +current offline foundation is the conceptual safe state, but a future release must identify +and verify its exact artifact rather than rely on that phrase. + +## 6. Synthetic evaluation matrix + +The fixture set contains 34 cases: + +- one structurally complete synthetic case returns + `eligible_for_external_go_no_go_review_only`; +- 33 cases return `hold`. + +The hold cases cover: + +- the committed empty template; +- draft state or no review request; +- attempted runtime activation; +- missing facility, unit, or cohort scope; +- missing or inconsistent inclusion/exclusion releases; +- zero or excessive cohort count; +- automatic enrollment; +- absent or malformed language scope; +- missing interpreter coverage or attempted fallback; +- non-denying unknown/outage exclusion results; +- missing timezone, support windows, or urgent-help release; +- overlapping support windows; +- future, expired, or overlong validity; +- missing audit deployment; +- mutable/incomplete/content-bearing audit policy; +- missing rollback evidence; +- a released default kill switch; and +- a missing named approver. + +Fixtures contain no real patient, principal, encounter, staff, facility, unit, approval, +source, support, or release identifiers. The complete fixture is a structural test, not a +recommendation or authorization. + +## 7. Negative verifier mutations + +The `--self-test` mode exercises 25 independent artifact mutations, including attempts to: + +- mark the candidate approved; +- enable pilot activation; +- populate the committed default scope, validity, or approval; +- alter source checksums; +- convert the limited positive disposition into activation; +- widen validity or cohort ceilings; +- remove approval/audit inventories; +- change case counts or expected outcomes; +- insert a patient identifier; +- enable runtime activation or automatic enrollment; +- permit audit clinical content; +- release the default kill switch; +- permit post-expiry disclosure; +- weaken uncovered-support behavior; +- expose exclusion reasoning; or +- allow unapproved language fallback. + +Every mutation must fail. The verifier also checks the four source hashes, the zero-path +contract, every false executable activation fact, the code-owned negative backend +defaults, and the absence of production hosts and credentials. + +## 8. Relationship to the existing activation gate + +The existing five-state activation gate and this manifest solve different problems: + +| Boundary | Question answered | +| -------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------- | +| Activation-separation gate | Are clinical approval, content release, feature activation, pilot enrollment, and source deployment represented as distinct positive states? | +| Controlled-pilot manifest | Is the proposed pilot scope exact, bounded, supported, expiry-limited, approved by all required roles, auditable, and rollback-linked? | + +Neither boundary authorizes access. A future operation would still need identity, +current-encounter binding, relationship, operation scope, content release, freshness, +language, correction/retraction, audit-before-disclosure, serialization, and generic +non-disclosure evaluation. + +The manifest does not change `pilot_enrollment_state` from `not_enrolled`, and it does not +make the all-positive activation row reachable. + +## 9. Patient, production, and sample-record boundary + +This work performed no database access. It neither reads nor uses the authorized +Nightingale sample principal or encounter. + +The sample remains: + +- pending and inactive; +- contactless and passwordless; +- without an identity link, grant, challenge, session, projection, or native caller; and +- unrelated to the synthetic cohort fixture. + +No facility, unit, or cohort handle in the manifest can be resolved to a production row. +No migration, route, query, notification, feature flag, enrollment, content release, +deployment, or activation occurred. + +## 10. Future implementation requirements + +Before any runtime manifest service exists, a separately reviewed change must define: + +- the signed/canonical serialization and immutable digest; +- authoritative facility/unit handle issuance and non-enumerability; +- cohort and exclusion policy contract semantics; +- language registry, translated content releases, interpreter-service status, and fallback; +- timezone, daylight-saving, exception-calendar, and support escalation behavior; +- named-approval authority, independence, signature, revocation, and expiry; +- durable audit storage, actor hashing, retention, access, alerting, and reconciliation; +- kill-switch propagation, bounded convergence time, rollback artifact identity, and proof; +- generic non-disclosure for every manifest lookup/evaluation error; +- race behavior for transfer, discharge, expiry, withdrawal, source outage, or approval + revocation; +- idempotent enrollment and removal semantics; +- production-like failover, load, downtime, and rollback exercises; and +- native behavior on expiry, withdrawal, offline state, and unavailable support. + +Those requirements must be backed by approved contracts and independent review. They must +not be inferred from this candidate or implemented by wiring the JSON directly into +Laravel. + +## 11. Verification command + +From the repository root: + +```bash +node scripts/ci/verify-nightingale-controlled-pilot-manifest.mjs . --self-test +``` + +Expected result: + +```text +Nightingale controlled-pilot manifest verified: 34 synthetic cases (33 hold, 1 external go/no-go review only), 4 checksum-bound sources, 25 negative self-tests. +``` + +The verifier is included in the Nightingale contract job and the native product-boundary +chain. Exact-SHA CI remains required for publication. + +## 12. Remaining holds + +This foundation does not approve or implement: + +- a real facility, unit, cohort, locale, exclusion policy, or support schedule; +- identity proofing, recovery, representatives, consent, or sensitive-service policy; +- clinical content, Today, My Path, Care Team, education, discharge, or communication; +- a source connector, audit sink, manifest repository, evaluator, or enrollment service; +- human patient-advisor, accessibility, language, clinical, legal, privacy, or security + review; +- a signed artifact, App Store/Play distribution, physical-device acceptance, or support + operation; +- production-like integration, load, failover, outage, monitoring, or rollback exercises; +- a pilot go/no-go decision; or +- merge, deployment, migration, production mutation, or activation. + +The next Stream F items remain open. diff --git a/docs/nightingale/ENCOUNTER-ACCESS-CANDIDATE-DECISION-2026-07-26.md b/docs/nightingale/ENCOUNTER-ACCESS-CANDIDATE-DECISION-2026-07-26.md new file mode 100644 index 00000000..66b62255 --- /dev/null +++ b/docs/nightingale/ENCOUNTER-ACCESS-CANDIDATE-DECISION-2026-07-26.md @@ -0,0 +1,541 @@ +# Nightingale encounter-access candidate decision + +**Status:** Held candidate and synthetic fixture specification only. The product namespace, +relative candidate path, and operation ID are reserved by the foundation ADR, but this record +does not add an OpenAPI operation, register a route, enable identity or networking, approve a +source adapter, authorize patient disclosure, or permit production use. + +**Decision date:** 2026-07-26 + +**Candidate artifacts:** + +- [`candidate.json`](./api-contract/candidates/encounter-access/v0/candidate.json) +- [`fixtures.json`](./api-contract/candidates/encounter-access/v0/fixtures.json) + +**Governing records:** + +- [Contract ownership and authorization matrix](./CONTRACT-OWNERSHIP-AND-AUTHORIZATION-MATRIX-2026-07-26.md) +- [Patient-state vocabulary classification](./PATIENT-STATE-VOCABULARY-CLASSIFICATION-2026-07-26.md) +- [Route, compatibility, identity, and inpatient-source ADR](./ROUTE-COMPATIBILITY-IDENTITY-SOURCE-ADR-2026-07-26.md) +- [Empty/default-off contract foundation](./api-contract/nightingale-foundation.v0.json) + +## 1. Outcome + +The first Nightingale read candidate is the patient need currently represented by legacy +`GET /encounters`: discover whether the authenticated person has a currently eligible +inpatient context that Nightingale may use for later, separately authorized experiences. + +The candidate deliberately does less than the legacy operation: + +- it returns zero or one Nightingale-owned opaque `encounter_handle`; +- it supports only the `self` relationship; +- it exposes no grant identifier, relationship code, authorization scope, validity window, + source identifier, or database row version; +- it does not claim that an empty result means the person is not in a hospital; +- it rejects an ambiguous multi-context state instead of silently selecting the first row; +- it requires a verified identity link and authoritative current inpatient-context check; +- it requires request-level audit even for an empty success and a separate durable disclosure + event for a returned handle; and +- it remains outside the executable OpenAPI artifact until every pre-operation gate is + approved. + +The candidate fixtures define exact success, omission, denial, ambiguity, integrity, +dependency, audit, race, and throttling outcomes. A CI verifier checks the fixtures while +also proving that the runnable Nightingale contract still has zero paths. + +## 2. Source snapshot + +The analysis used the feature branch at `a1cc3a83`, whose merge base contains +`origin/main` commit `84b5f830`. + +| Source | SHA-256 | Evidence used | +| ----------------------------------------------------------------------------------------- | ------------------------------------------------------------------ | ------------------------------------------------------------------------ | +| `routes/patient.php` | `e892a9d570668bec6ae54d18cbfd891f7d9df2e2c2f8f6e3689125d6b465af28` | Legacy product/feature/auth/realm/ability/rate middleware | +| `app/Http/Controllers/Api/Patient/EncounterController.php` | `3de0a2edeb487a890dba2166791f5c36b1ce97d9d82ec1967054d0fad251c81b` | Query, per-grant audit, envelope metadata | +| `app/Services/Patient/PatientEncounterAccessService.php` | `a593503e20dc8ba45699af4974f36345a0e85b739fa2f428565c8d3d4b079ef9` | Grant filtering, sorting, legacy projection fields | +| `app/Policies/Patient/PatientEncounterAccessGrantPolicy.php` | `61ca77c6713c23be4e3ab522edf9f279b517aef52d5d75ebf68d5a1b9d4fd58c` | Active-principal and grant ownership/effective-window checks | +| `app/Models/Patient/PatientEncounterAccessGrant.php` | `237908876e22fe2c3fcd6e55cedde87046288adb4ddced43368273acf97934e5` | Storage fields, casts, defaults, query scope | +| `database/migrations/2026_07_19_000100_create_patient_experience_identity_foundation.php` | `f093d43a7a84b00ee519f6b1392a8aee5018ab1f43d930ea5abf1c4d7ecaa1e2` | Database constraints, uniqueness, effective dates, source linkage | +| `tests/Feature/Patient/PatientApiBoundaryTest.php` | `2235aca6ad0c2b59c43eb5db7b2eff3d74351b47ae98f4d0528a0cd13b1f2508` | Authentication, default-off, single active row, forbidden linkage fields | +| iOS `PatientAPIModels.swift` | `93c3250b7292c9abe17e3bb932e39c489332c8becc321663b4b8365a1e468759` | Legacy encounter decoder/model | +| iOS `PatientAppViewModel.swift` | `74918da1c9af1a263b4535dec933c4c6acf4384bb7afa195997a26e5e1c37694` | First-row selection and downstream request fan-out | +| Android `PatientApiModels.kt` | `a1932c3dc2f037f4006c72e083d57bc6219f63de42f348c7909737707d25e115` | Legacy JSON decoder/model | +| Android `PatientSessionCoordinator.kt` | `10bfbe8782746390ea71242d1c95ff5f09809b61be40533f91cba1e9acfaada2` | First-row selection and scope-driven fan-out | + +The legacy OpenAPI input remains pinned by hash in the broader contract matrix. No database, +deployed configuration, production response, patient, grant, or session was read. + +## 3. Current reference flow + +The source path is: + +```text +legacy product flag + -> legacy encounters feature flag + -> Sanctum authentication + -> patient realm and exact token-name/ability check + -> active owned session and expiry checks + -> patient API throttle + -> viewAny(active principal) + -> query active, non-revoked, effective grants for principal + -> per-row view policy + -> per-row durable disclosure audit + -> serialize seven grant-derived fields +``` + +The response currently exposes: + +- `encounter_uuid`; +- `grant_uuid`; +- `relationship`; +- raw `scopes`; +- `valid_from`; +- `expires_at`; and +- grant-row `version`. + +The envelope adds count, maximum row version, a freshness claim based on maximum +`updated_at`, the draft disclosure-policy version, and the draft patient-state-vocabulary +version. Both native clients decode the seven fields and select `encounters.first` for all +subsequent projection requests. + +## 4. Material migration blockers + +### 4.1 The response overexposes authorization internals + +The native clients need a stable context handle; they do not need a grant UUID, raw server +scopes, authorization dates, relationship database value, or grant version to render a +pre-journey state. Returning those values: + +- increases correlation and inference surface; +- couples clients to server authorization vocabulary; +- encourages clients to treat cached scopes or dates as authority; +- complicates representative and sensitive-service policy; +- creates unnecessary compatibility obligations; and +- makes a server-side grant model appear to be a patient-facing domain model. + +The candidate therefore contains one field only: a separately issued Nightingale encounter +handle. + +### 4.2 “First row wins” is unsafe and product-incomplete + +Both legacy native clients silently select the first encounter. The backend sorts by +`valid_from DESC`, then grant UUID. This is not a patient decision, a clinical rule, or a +safe transfer/re-admission reconciliation rule. It can become ambiguous during: + +- facility transfer; +- merged/split encounters; +- overlapping observation and inpatient records; +- correction or re-registration; +- duplicate identity linkage; +- representative relationships; +- source lag; or +- an improperly closed prior grant. + +The initial Nightingale candidate supports zero or one eligible self inpatient context. More +than one is a governed `account_state_requires_review` error. A future multi-context design +requires a clinically released, patient-understandable selector—not ordering by an +authorization timestamp. + +### 4.3 Identity-link state is not part of list eligibility + +The legacy grant can carry `identity_link_id`, but list filtering and policy do not require: + +- a non-null identity link; +- verified status and verification time; +- ownership by the same principal; +- non-revoked state; +- non-merged/non-superseded state; or +- an assurance level adequate for the operation. + +Nightingale cannot disclose even an opaque handle until the current identity relationship +is proven at request time. + +### 4.4 Current inpatient state is not authoritatively confirmed + +The legacy list checks grant state and dates but not the linked operational encounter's +current state. `source_freshness.status` becomes `current` whenever any grant is returned, +even though “current” only describes grant-row presence/update time. It does not prove that +the patient remains admitted, that a transfer is reconciled, or that the source is healthy. + +The candidate distinguishes: + +- authoritative confirmation of an eligible current inpatient context; +- authoritative confirmation that a context is closed, which is omitted; +- missing or inconsistent source linkage, which requires account review; and +- source unavailability, which returns temporary unavailability instead of a false empty + state. + +### 4.5 Effective-window semantics drift + +The database defines `valid_from` as non-null with a default. The service and policy still +accept null `valid_from`, and the legacy OpenAPI explicitly permits null. The model's +`scopeEffective` does not accept null. The iOS reference fixture also supplies null. These +are four different expectations around a security-relevant time boundary. + +Nightingale requires a non-null effective start in storage and evaluates all timestamps +against one server-controlled `authorization_evaluated_at`. Clock comparisons never occur +in the client. + +### 4.6 Scope and enum integrity are under-specified + +The database checks only that `scopes` is a JSON array. It does not constrain each member to +an approved string. The list service returns the raw array; the OpenAPI regex allows a broad +set of strings; both clients use strings to decide which endpoints to call. + +The candidate does not return scopes. A malformed or unknown server scope registry is a +data-integrity failure, and every downstream operation must reauthorize server-side. Future +patient-facing availability indicators need their own product vocabulary and cannot be raw +authorization scopes. + +### 4.7 Collection version and freshness are not coherent + +The maximum per-grant row version is not a monotonic collection version. Adding, revoking, +or omitting a row can leave it unchanged or reduce it. Likewise, maximum `updated_at` does +not establish completeness or source freshness. The candidate uses neither. It records: + +- the exact policy version; +- request generation time; +- one authorization evaluation time; and +- whether the result is complete or withheld. + +If a future conditional request is needed, it requires a server-generated collection +revision bound to the complete eligible set and policy—not `max(version)`. + +### 4.8 Audit coverage is row-oriented + +The legacy implementation records one event per returned grant. An empty successful lookup +records no equivalent evaluation event, even though the result is patient-sensitive and can +affect care navigation. The candidate requires: + +- one durable request-level evaluation event for every 200 response, including empty; +- one durable handle-disclosure event for the single returned handle; +- no success if either required audit fails; +- best-effort denial audit where a failing denial sink must not become an availability + oracle; and +- no raw handles, source identifiers, free text, or credentials in audit metadata. + +### 4.9 Native decoders do not enforce the contract equally + +The iOS model decodes strings and integers but performs no encounter/grant UUID, enum, scope, +or date validation after decode. Android likewise uses raw strings and only performs limited +date validation elsewhere for sessions. Both clients trust the first list element. + +Future Nightingale decoders must validate exact fields and types, reject more than one item, +reject unknown fields for the initial contract, validate the Nightingale handle format, and +withhold the whole context on a malformed response. There is no partially trusted row. + +### 4.10 Reference test coverage is too narrow for ratification + +The principal backend feature test proves one active row is returned and selected source +linkage fields are absent. It does not comprehensively prove: + +- wrong-principal exclusion; +- every status and time boundary; +- identity-link ownership/status; +- representative handling; +- purpose of use; +- source encounter status or outage; +- multiple eligible rows; +- malformed registry data; +- transaction/race revalidation; +- empty-result audit; +- per-handle audit failure; +- stable ordering or bounded cardinality; +- exact response-field allowlisting; or +- iOS/Android parity on malformed list responses. + +The candidate fixture matrix makes these omissions explicit before implementation starts. + +## 5. Candidate product semantics + +### 5.1 Purpose + +The operation answers one narrow question: + +> Is there exactly one currently verified self inpatient context that this signed-in +> Nightingale principal may use as the starting point for separately authorized patient +> experiences? + +It does not answer: + +- whether the person is clinically admitted in every source; +- which facility/unit/room they occupy; +- which projections exist; +- what care they are receiving; +- which messages they may send; +- whether a representative has access; +- why access is absent; or +- whether a closed, transferred, corrected, or disputed record exists. + +### 5.2 Initial cardinality + +| Eligible contexts after all gates | Candidate outcome | Patient-safety reason | +| ---------------------------------------------------: | ---------------------------------------------------------------------------------------------- | --------------------------------------------------------------------- | +| 0 | `200` with an empty array, but only after complete authoritative evaluation and durable audit | Does not invent a stay or disclose an ineligible record | +| 1 | `200` with one Nightingale opaque handle after recheck and durable evaluation/disclosure audit | Supplies only the minimum navigation capability | +| 2 or more | `409 account_state_requires_review`, no handles | Prevents arbitrary first-row selection and cross-encounter disclosure | +| Unknown because a required dependency is unavailable | `503 temporarily_unavailable`, no handles | Avoids falsely representing “no stay” | + +The patient UI for an empty result must say that no current stay is available **in +Nightingale right now**, not that the patient is not admitted. It must offer a bedside/support +path. It may not distinguish revoked, wrong-principal, held representative, closed, expired, +or otherwise omitted records. + +### 5.3 Relationship + +Only `self` is a candidate. Recognized representative relationships are omitted without +confirming their existence. Unknown relationship codes are integrity failures. No +representative row, copy, enrollment behavior, patient field, or communication power is +approved by this decision. + +### 5.4 Purpose of use + +The exact inpatient purpose code is not yet approved, so it remains an eligibility gate +rather than a committed wire value. A recognized non-inpatient purpose is omitted. An +unknown purpose is an integrity failure. Environment variables cannot define or approve +purpose codes. + +## 6. Field decision + +| Field | Legacy behavior | Candidate decision | Rationale | +| --------------------------- | ------------------------------------------------ | ------------------------------------ | -------------------------------------------------------------------------------------- | +| `encounter_uuid` | Returned and used as route handle | Reject as a cross-product identifier | Nightingale requires a separately issued handle with no implicit legacy/source linkage | +| `grant_uuid` | Returned | Omit | Client never authorizes itself by grant | +| `relationship` | Returned as raw enum | Omit; candidate is self-only | No current display or navigation need; representative model held | +| `scopes` | Returned and drives native requests | Omit | Raw server authorization vocabulary is not a patient-facing capability contract | +| `valid_from` | Returned, nullable in contract/client | Omit | Server evaluates; client clocks/caches must not authorize | +| `expires_at` | Returned | Omit | Server evaluates on every request; expiry may change/revoke earlier | +| `version` | Per-grant integer returned; max used in metadata | Omit | Not a coherent collection revision | +| `encounter_handle` | Does not exist as Nightingale-owned value | Sole entry field | Minimum opaque navigation handle | +| location/facility/unit/room | Not in list | Do not add | Must come from a separately released patient projection | +| admission/discharge/status | Not in list | Do not add | Clinical/operational status requires source and patient-language governance | +| available surfaces | Raw scopes imply them | Do not add yet | Needs an approved product capability vocabulary and operation-specific authorization | + +## 7. Opaque handle requirements + +The fixture format is `ntg_enc_` followed by 50 independently generated lower-case base32 +symbols (250 bits). Generation must use a cryptographically secure random source without +modulo bias. The prefix identifies only the Nightingale resource type; the random body must +contain no time, facility, patient, source system, database key, grant identifier, or +checksum derived from those values. + +Before implementation, the handle design must prove: + +- cryptographically secure generation and collision handling; +- case-sensitive canonicalization with no client rewriting; +- one-to-one mapping to the governed authorization record; +- mapping ownership and relationship binding; +- no reuse across principals, representatives, products, environments, or restored + databases; +- revocation, transfer, merge, correction, and reissue behavior; +- transactional uniqueness; +- HMAC/digest-only audit/search where raw recording is unnecessary; +- no appearance in logs, analytics, crash reports, push payloads, deep links, screenshots, + clipboard, or support tickets; and +- separate non-production and production namespaces. + +The fixture handle is intentionally synthetic and repetitive. It validates shape only and +must never be replayed into an environment. + +## 8. Eligibility and serialization lattice + +```text +candidate becomes an approved operation in an active Nightingale contract + -> product, operation, facility, unit/cohort, and pilot gates are effective + -> ingress, media, request-size, rate, and TLS policy pass + -> Nightingale realm principal and owned session are active at required assurance + -> identity link is verified, current, unmerged, and owned by the principal + -> candidate grant belongs to principal and identity link + -> relationship is exactly self + -> purpose is the approved inpatient purpose + -> grant is active, non-revoked, and effective at one server time + -> source linkage is complete and current inpatient state is confirmed + -> Nightingale handle mapping is valid and unique + -> exactly zero or one candidate remains + -> authorization state is rechecked at serialization boundary + -> durable evaluation audit succeeds + -> durable handle audit succeeds when one handle is returned + -> exact no-store envelope is serialized +``` + +Every unknown status, relationship, purpose, policy version, mapping state, or scope-registry +value fails closed. A client-provided handle, scope, date, relationship, or source value can +never enter this decision. + +## 9. Outcome equivalence classes + +The 42 synthetic cases are executable documentation. Key equivalence rules are: + +| Class | Examples | Exact result | +| ----------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------- | +| Complete omission | no grants, revoked/suspended/pending/closed/expired, future/expired window, wrong principal, held representative, recognized purpose mismatch, source-confirmed closed | `200 success_empty`; one durable evaluation audit; no reason exposed | +| Release unavailable | product or operation disabled | `404 not_found`; best-effort denial audit | +| Authentication absent/invalid | no credential; missing/expired/revoked session | `401 authentication_required`; no account/grant detail | +| Realm/account denied | wrong realm, inactive or locked principal | `403 access_unavailable`; generic account-safe copy | +| Identity/source ambiguity | missing/revoked/merged/mismatched identity link, missing source link, multiple eligible contexts, authorization race | `409 account_state_requires_review`; no handles | +| Invalid governed data | unknown relationship/status/purpose, malformed/colliding handle, malformed scope registry, policy mismatch | `503 temporarily_unavailable`; no partial result | +| Dependency/audit outage | source unavailable, database unavailable, required audit unavailable | `503 temporarily_unavailable`; never translate outage to empty | +| Throttle | principal-specific limit exceeded | `429 rate_limited`; no existence information | +| Eligible | exactly one fully verified self inpatient context | `200 success_one`; request and disclosure audits | + +Wrong-principal and held-representative rows are deliberately indistinguishable from no +candidate row. Invalid data is not silently omitted because returning a partial “complete” +list could conceal a safety-relevant integrity problem. + +## 10. Exact candidate response + +The canonical one-handle success shape is: + +```json +{ + "data": { + "encounters": [ + { + "encounter_handle": "ntg_enc_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + } + ] + }, + "meta": { + "request_id": "ntg_req_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "generated_at": "2026-07-26T15:00:00Z", + "policy_version": "nightingale-encounter-access-policy.v0-candidate", + "authorization_evaluated_at": "2026-07-26T15:00:00Z", + "completeness": "complete" + }, + "links": {} +} +``` + +Required HTTP headers are: + +```text +Cache-Control: private, no-store, max-age=0 +Pragma: no-cache +X-Robots-Tag: noindex, nofollow, noarchive +Vary: Authorization +Content-Type: application/json +``` + +The candidate does not include `state_vocabulary_version`: it contains no patient-facing +state code or label. Adding irrelevant metadata would imply a dependency that does not +exist. It also includes no self link, because a route has not been assigned and an endpoint +URL is not patient data. + +## 11. Concurrency and audit requirements + +Implementation must define one evaluation snapshot or equivalent optimistic transaction: + +1. capture one server-controlled authorization time; +2. evaluate principal/session/identity/grant/relationship/purpose/source/handle state; +3. reject more than one eligible context; +4. recheck all mutable record versions immediately before serialization; +5. persist the request-level evaluation event; +6. persist the handle-disclosure event when applicable; and +7. serialize only after required audit commits. + +A source connector that cannot participate in the database transaction must return a +versioned/expiry-bounded confirmation that is revalidated under the approved source policy. +Downstream operations still repeat authorization; a prior list response never grants access. + +The request audit may record governed reason codes and internal foreign keys. Patient-visible +responses collapse them to the fixture templates. Raw handles, source identifiers, patient +names, care text, credentials, request bodies, IP addresses in application logs, and free +text are not permitted audit metadata. + +## 12. Future backend implementation requirements + +The namespace/default-deny foundation is implemented, but no runtime operation is approved. +A reviewable operation change would still need: + +- an approved Nightingale-owned contract operation under the reserved route namespace; +- a separate default-off Nightingale configuration boundary, not an alias to Hummingbird + flags; +- an approved Nightingale realm and session design behind the default-deny identity port; +- an allowlisted purpose/relationship/status/scope registry in code; +- a verified identity-link join and ownership assertion; +- an approved operational source adapter and current-inpatient definition behind the + default-deny source port; +- a new Nightingale handle mapping with uniqueness and revocation rules; +- a bounded query that cannot return more than the initial maximum; +- one consistent effective-time predicate shared by query and policy; +- deterministic transaction/recheck/audit behavior; +- exact response-resource classes rather than model serialization; +- generic errors and the exact no-store headers; +- no access to staff APIs or patient free text; and +- default-off non-production integration before any production consideration. + +The current `PatientEncounterAccessService` may inform tests, but it cannot simply be called +and its projection renamed. + +## 13. Future native implementation requirements + +Until the contract operation is approved, neither Nightingale app may contain its route, +network client, decoder, or fixture runtime. A future native slice must: + +- generate or implement models only from the approved Nightingale contract; +- accept an exact zero-item result and reject more than one item or any malformed item; +- accept only the exact handle pattern and exact response fields; +- never select `.first` as an ambiguity strategy; +- never persist the handle to preferences, files, logs, analytics, pasteboard, notifications, + deep links, or screenshots; +- clear the handle on logout, identity transition, recovery, revocation, account removal, or + an authentication-invalid response; +- treat a handle as a lookup capability, never proof of authorization; +- show the approved “not available in Nightingale right now” empty experience; +- show outage/account-review guidance without claiming the person is or is not admitted; +- avoid offline or cached continuation into patient data; +- pass identical malformed, unknown, zero, one, and multiple-item fixtures on iOS and + Android; and +- preserve the privacy cover and Android secure-window controls. + +## 14. Required automated evidence before contract inclusion + +Backend/contract tests must cover every fixture plus: + +- exact route absence while held and default-off behavior after introduction; +- staff, wrong-realm, wrong-principal, wrong-identity-link, and representative + non-disclosure; +- every grant status, revocation, and boundary-time comparison; +- exact purpose/relationship/status/scope registries; +- verified/merged/revoked identity links and ownership mismatch; +- source closed, transferred, corrected, stale, missing, and unavailable states; +- zero, one, duplicate, and multiple eligible contexts; +- handle generation entropy, collision, environment isolation, rotation, and revocation; +- transaction/race mutation between query, audit, and serialization; +- request-level and disclosure-audit success/failure; +- database, source, audit, and policy outages; +- exact allowlist serialization and forbidden-field/binary scans; +- no-cache headers for every success/error; +- rate limiting without cross-principal coupling; +- load bounds and query plan; +- iOS and Android fixture parity; and +- logout/recovery/revocation clearing with no durable handle residue. + +Testing must use synthetic records in isolated test databases. Production patient creation +or replay of these fixtures is prohibited. + +## 15. Open decisions and approvals + +- [ ] Name the accountable product, contract, backend, identity, privacy/security, + accessibility, support, clinical-safety, source-integration, and release owners. +- [x] Adopt and mechanically pin the `/api/nightingale/v1` namespace, + `/inpatient-contexts` relative candidate path, operation ID, and no-alias/no-proxy/ + no-redirect compatibility strategy. Runtime registration and independent release + approval remain prohibited. +- [ ] Approve identity proofing, assurance, session, recovery, and representative model. +- [ ] Define the exact eligible inpatient context across admission, observation, transfer, + leave, discharge, correction, downtime, and merge. +- [ ] Approve the purpose/relationship/status/scope registries. +- [ ] Approve opaque handle generation, storage, rotation, collision, environment, logging, + and revocation design. +- [ ] Resolve zero/one initial pilot bounds and the future multi-context experience with + patient advisors and operational owners. +- [ ] Approve error and empty-state language through patient, accessibility, language, + support, privacy, and clinical-safety review. +- [ ] Implement and independently review all backend/contract/native tests. +- [ ] Complete threat model, penetration testing, non-production integration, rollback, and + exact-SHA release evidence. + +Until every applicable gate is satisfied, the Nightingale OpenAPI `paths` object remains +empty, no Laravel route is registered, and native network access remains disabled. The +reserved path and operation ID are design identifiers only. diff --git a/docs/nightingale/FOUNDATION-ACCESSIBILITY-LANGUAGE-READINESS-2026-07-27.md b/docs/nightingale/FOUNDATION-ACCESSIBILITY-LANGUAGE-READINESS-2026-07-27.md new file mode 100644 index 00000000..68da4409 --- /dev/null +++ b/docs/nightingale/FOUNDATION-ACCESSIBILITY-LANGUAGE-READINESS-2026-07-27.md @@ -0,0 +1,340 @@ +# Nightingale foundation accessibility and language-readiness evidence + +**Date:** 2026-07-27 + +**Product:** Nightingale + +**Scope:** bounded offline native shell only + +**Evidence class:** non-PHI engineering evidence; not accessibility conformance, translation +approval, clinical approval, distribution approval, or live-use authorization + +## 1. Decision + +The current Nightingale offline shell now has a mechanically governed source-language +contract and native language-readiness exercises on iOS and Android. The completed subset +is deliberately narrow: + +- 15 exact, nonclinical English strings are identical across both native products; +- every visible foundation string is loaded through the native localization resource + system; +- the current shell exposes three ordered headings; +- two preference status changes use restrained screen-reader notification behavior; +- iOS exercises rendered text doubling and a Debug-only forced right-to-left layout; +- Android exercises the platform `en-XA` text-expansion and `ar-XB` bidirectional + pseudolocales; +- every current control remains reachable and operable during the exercised states; and +- Release artifacts contain the governed English source only and exclude Debug + pseudolocales or layout test hooks. + +This does **not** approve a non-English locale. It does not establish WCAG 2.2 AA +conformance, VoiceOver or TalkBack human usability, translation quality, interpreter +workflow, patient comprehension, or accessibility of future patient-data screens. + +## 2. Triggering audit + +The previous accessibility/layout matrix closed maximum text, contrast, semantic order, +landscape, and target-size behavior for the offline shell, but retained these gaps: + +1. the privacy title on Android had no heading semantics; +2. changing display-comfort status had no explicit restrained announcement policy; +3. most visible shell copy was hardcoded in Swift and Kotlin; +4. iOS and Android had no exact cross-platform copy reconciliation; +5. no native journey exercised rendered text expansion; +6. no native journey exercised right-to-left layout or bidirectional markers; and +7. Release verifiers did not reject accidentally shipped pseudolocales. + +The live Today route, clinical projection, identity, and communication work remains held. +Closing language readiness on the existing nonclinical shell is independent of those +unapproved capabilities. + +## 3. Authoritative platform inputs + +The implementation uses platform behavior described by: + +- Apple, + [Preparing your interface for localization](https://developer.apple.com/documentation/xcode/preparing-your-interface-for-localization); +- Apple, + [Testing your internationalized app](https://developer.apple.com/library/archive/documentation/MacOSX/Conceptual/BPInternational/TestingYourInternationalApp/TestingYourInternationalApp.html); +- Apple, + [Localization](https://developer.apple.com/localization/); +- Android, + [Test your app with pseudolocales](https://developer.android.com/guide/topics/resources/pseudolocales); + and +- Android, + [Semantics in Compose](https://developer.android.com/develop/ui/compose/accessibility/semantics). + +These references inform engineering test design. They are not evidence that Nightingale +has completed a platform accessibility certification or human review. + +## 4. Exact governed copy contract + +The iOS source language is `en` in `Localizable.xcstrings`. Android carries the same +records in `res/values/strings.xml`. Neither source contains another approved +localization. + +| Stable key | Exact English source value | +| ----------------------------------- | ----------------------------------------------------------------------------------------------------------------- | +| `app_name` | Nightingale | +| `foundation_mission` | A calm place to understand, prepare, and connect with your care team. | +| `privacy_heading` | Your privacy comes first | +| `foundation_unavailable` | Live patient access is not available in this foundation build. Please ask your care team for current information. | +| `foundation_no_patient_data` | No patient information is stored or requested by this build. | +| `display_comfort_heading` | Display comfort | +| `display_comfort_scope` | These settings are stored by Nightingale, not your care account. They never change your care information. | +| `reduce_motion_label` | Reduce motion in Nightingale | +| `motion_reduced_status` | Motion is reduced. Nightingale changes views without decorative movement. | +| `motion_standard_status` | Gentle transitions are enabled. Nightingale also follows your system Reduce Motion setting. | +| `hide_imagery_label` | Hide decorative imagery | +| `imagery_shown_status` | A calming Nightingale background is shown softly behind the page. | +| `imagery_hidden_status` | Decorative imagery is hidden. Essential text and controls remain available. | +| `privacy_cover_accessibility_label` | Privacy cover. Your care information is hidden while Nightingale is not active. | +| `privacy_cover_message` | Your care information is covered while the app is not active. | + +`verify-nightingale-foundation-copy.mjs` parses both source formats and compares exact +key/value records. It also rejects: + +- a missing or extra key; +- copy drift between platforms; +- an iOS locale other than `en`; +- Android Release pseudolocales; +- a return to hardcoded runtime copy; +- missing heading semantics; +- assertive Android status announcements; +- a missing iOS double-length journey; and +- a missing Android `ar-XB` journey. + +The verifier currently has nine negative mutations. It is invoked by the existing +Nightingale product-boundary gate, so CI cannot pass by running only the native compilers. + +## 5. Semantic and announcement policy + +### 5.1 Headings + +The current shell has exactly three ordered headings on each platform: + +1. Nightingale; +2. Your privacy comes first; and +3. Display comfort. + +iOS applies the header accessibility trait. Android applies Compose heading semantics, +including the privacy heading that was previously plain text. + +### 5.2 Status changes + +The reduced-motion and decorative-imagery status values change only after a user operates +the corresponding switch. + +- Android exposes exactly two `LiveRegionMode.Polite` semantics and prohibits + `LiveRegionMode.Assertive`. +- iOS posts one localized announcement per operated preference through + `UIAccessibility`, with `UIAccessibilityPriority.low`. The low priority queues the + announcement rather than overriding more important speech. + +No initial page-load announcement, repeating timer, decorative-image announcement, or +assertive notification was added. + +## 6. iOS implementation and test design + +### 6.1 Source and Release boundary + +- `NightingaleCopy.swift` owns stable localization keys and the low-priority announcement + adapter. +- `Localizable.xcstrings` owns the exact English source. +- SwiftUI views consume `LocalizedStringKey` values rather than patient-visible literals. +- `#if DEBUG` contains the accessibility text-size and right-to-left layout test adapters. +- the Release bundle must contain exactly `en.lproj`; +- the Release bundle's compiled `Localizable.strings` must parse to the exact 15 values; + and +- the Release binary scan continues to prohibit test environment hooks. + +UIKit is now a declared Apple system-module import for the low-priority accessibility +announcement. The governed dependency inventory was regenerated and records five Apple +system modules and zero third-party iOS packages. + +### 6.2 Double-length journey + +The UI test launches with `-NSDoubleLocalizedStrings YES`, accessibility text size, and a +landscape viewport. It requires: + +- the rendered mission label to be longer than the English source; +- both current switches to exist; +- each switch to retain a minimum 44-point frame; and +- each switch to change state after an accessibility-driven tap. + +The retained screenshot visibly doubles all localized strings. The exercise is a +pseudolanguage stress state, not approved patient copy. + +### 6.3 Right-to-left journey + +An Arabic locale and Apple right-to-left launch arguments alone did not mirror this +English-only SwiftUI bundle. The accepted test therefore also uses a compile-time +Debug-only `layoutDirection` adapter. The test compares the same heading in left-to-right +and right-to-left launches and requires: + +- the heading's leading edge to move by more than 40 points; +- all five required semantic landmarks and controls to retain their logical order; and +- the mirrored decorative-imagery switch to change state. + +The retained screenshot shows right-aligned English fallback copy. English fallback is +intentional because no Arabic translation is approved. It proves current layout +adaptability, not Arabic usability or translation quality. + +## 7. Android implementation and test design + +### 7.1 Build-type boundary + +Gradle enables pseudolocales only for Debug: + +- Debug: `isPseudoLocalesEnabled = true`; +- Release: `isPseudoLocalesEnabled = false`. + +The generated Debug APK contains `en-rXA` and `ar-rXB`. The Release verifier rejects those +configurations and the BCP-47 variants. Release contains all 15 English compiled resource +keys. + +Android's `ar-XB` resource strings inserted bidirectional markers, but the +instrumentation-owned Compose root did not reliably inherit a mirrored direction. +Nightingale therefore has two build-type implementations of one language-readiness +policy: + +- Debug maps only `ar-XB` to `LayoutDirection.Rtl`; +- Release always returns the platform-selected direction and contains no `ar-XB` literal. + +The actual tagged content shell is inside the composition-local direction provider. This +keeps the pseudolocale correction out of Release while requiring the Debug journey to +exercise mirrored layout. + +### 7.2 `en-XA` exercise + +The API 35 instrumentation journey selects the app-specific `en-XA` locale, recreates the +activity, and requires: + +- the mission resource to differ from English; +- the mission to expand beyond the English source length; +- the expanded mission to remain displayed; and +- the decorative-imagery switch to remain reachable and off. + +The retained hierarchy contains accented, bracketed, expanded copy and filler words. It +also shows reflow into a taller initial viewport without truncating the safety message. + +### 7.3 `ar-XB` exercise + +The same journey selects `ar-XB`, recreates the activity, and requires: + +- the tagged shell's Compose layout direction to be RTL; +- all five semantic landmarks and controls to preserve logical order; +- bidirectional pseudolocale strings to remain rendered; +- the decorative-imagery switch to remain scroll-reachable; and +- the switch to change from off to on. + +The retained hierarchy shows the Nightingale heading at bounds +`[470,473][953,589]` in `ar-XB`, compared with `[127,473][953,705]` in the expanded +left-to-right hierarchy. The exact fonts and heights differ because the pseudolocales +intentionally transform text, so the native semantic assertion—not a pixel subtraction—is +the acceptance gate. + +Android `FLAG_SECURE` remains enabled. The external screenshot is entirely black. The +hierarchies are retained as the inspectable visual/semantic evidence; capture protection +was not weakened for audit convenience. + +## 8. Accepted verification results + +| Layer | Accepted result | +| ----------------------------------------- | ------------------------------------------------------------------- | +| Copy contract and negative self-tests | 15/15 exact keys; 9/9 negative mutations rejected | +| iOS signed Debug unit tests | 11/11 passed | +| iOS signed Debug UI tests | 6/6 passed; includes double-length and RTL journeys | +| iOS unsigned Release build/verifier | passed; exactly one English localization directory and no test hook | +| Android Debug unit tests | 8/8 passed | +| Android Release unit tests | 8/8 passed | +| Android API 35 instrumentation | 10/10 passed; includes exact semantics and both pseudolocales | +| Android Debug/Release lint and assembly | passed | +| Android unsigned Release APK verifier | passed; exact English keys and no `en-XA`/`ar-XB` configuration | +| Product-boundary and Xcode project checks | passed | + +Accepted environments: + +- Xcode 26.3, iPhone 16e simulator, iOS 26.3.1; +- Android `hb` AVD, Android 15/API 35; and +- local Debug and unsigned Release artifacts only. + +## 9. Exact artifact and retained-evidence binding + +| Artifact or evidence | SHA-256 | +| ----------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------ | +| iOS Debug simulator executable | `7128de99c084d8b3416aadb6c6cd716ea8ed82727e4d8d012ce4c2d5054c5d1c` | +| iOS Release simulator executable | `3667f3c743ec7b5ddce915beef2f0c96ead628d2d166d563de2f7e6bf3a1b698` | +| iOS Release compiled English strings | `8baee2535f701a01a0e5f972c3d531f057fa6fe85ea535e10694c9a40a2f947c` | +| Android Debug APK | `6511676a6ce092b2a868b1bc6f28027f7a60dd13f27da1e994a74473fdab2b47` | +| Android unsigned Release APK | `28b055b8873f8db9cf98a24edacdbf74349fbb6c6fd013175df12bca4e7f673e` | +| [iOS double-length screenshot](../evidence/nightingale/accessibility-language-readiness-2026-07-27/screenshots/ios-debug-double-length.png) | `05023f1954ec978e478bd79c6a9d5c0a1c0dc7fa8cd2838de7994011d6dafbdd` | +| [iOS RTL screenshot](../evidence/nightingale/accessibility-language-readiness-2026-07-27/screenshots/ios-debug-rtl.png) | `e7c0c62341732910930f5030587a2a2f7f7e99158764c15f0e2edd5b91387ba7` | +| [Android secure RTL screenshot](../evidence/nightingale/accessibility-language-readiness-2026-07-27/screenshots/android-debug-ar-xb-secure.png) | `c35bacdb98b522206335afa5b9baffd2e4e3352a40749bb747e469cd403af514` | +| [Android `en-XA` hierarchy](../evidence/nightingale/accessibility-language-readiness-2026-07-27/hierarchies/android-debug-en-xa.xml) | `3bbf3178a99659de488df1d7184cfb3e552ca418159ff5d8cd0301a2e770794a` | +| [Android `ar-XB` hierarchy](../evidence/nightingale/accessibility-language-readiness-2026-07-27/hierarchies/android-debug-ar-xb.xml) | `720d2c8d9b31e3f850dbee46def8e1e139f797a30b242e396d58d8bc749f22fc` | + +These are local engineering artifacts, not App Store or Play Store binaries, distribution +signatures, physical-device results, or pilot artifacts. + +## 10. Corrected preliminary failures + +The following failures shaped the accepted implementation and are not counted as passing +evidence: + +1. Android instrumentation initially used an unavailable Compose + `SemanticsConfiguration.getOrNull` helper. It was replaced with + `getOrElseNullable`, and all variants were recompiled. +2. SwiftUI in the current SDK did not expose the attempted + `accessibilityLiveRegion` modifier. iOS now uses low-priority localized + `UIAccessibility` announcements after user operations. +3. Apple language and text-direction launch arguments alone did not mirror the + English-only SwiftUI bundle. A compile-time Debug-only layout-direction adapter was + added and the journey now proves changed geometry. +4. The first iOS RTL switch assertion assumed an initial persisted state. The accepted + assertion records the state and proves it changes, regardless of prior nonclinical + presentation preference. +5. Android's first locale helper timed out when restoring an empty app-locale selection, + because system fallback is nonempty. The accepted helper separately checks app locale + selection and resource locale behavior. +6. Android's `ar-XB` resource markers did not change the Compose tagged shell direction + under instrumentation. Build-type-specific policy now forces only the Debug + pseudolocale, while Release remains platform-directed and contains no `ar-XB`. +7. The first Android direction assertion inspected the test framework's outer semantic + root, which is outside Nightingale's direction provider. The accepted assertion + inspects `nightingale-safe-shell`, the actual product content. +8. One intermediate Compose edit omitted an outer brace and failed both Debug and Release + compilation. The source was corrected and both variants plus all tests were rebuilt. + +## 11. Remaining gates + +The following remain open and cannot be inferred from this evidence: + +- human VoiceOver and TalkBack traversal, speech quality, focus recovery, and verbosity; +- Switch Control, Voice Control, keyboard, external input, magnification, and cognitive + accessibility review; +- a complete WCAG 2.2 AA audit across future screens and states; +- approved Arabic or any other translation; +- translation memory, linguistic QA, plain-language review, reading-level target, + cultural review, interpreter escalation, and locale fallback policy; +- bidirectional behavior for clinical values, dates, times, units, names, identifiers, + mixed scripts, charts, tables, messages, notifications, and error states; +- physical-device, supported-OS, backup/restore, upgrade, signing, store, and distribution + evidence; +- named patient-advisor, accessibility, language/interpreter, clinical, privacy/security, + legal/HIM, support, and release approvals; and +- every live identity, source, Today projection, messaging, notification, or patient-data + capability. + +The broad Stream E accessibility-conformance and human-validation checklist items remain +unchecked. + +## 12. Safety and non-authorization statement + +No clinical or patient-data UI was added. No route, provider, network client, source +adapter, database query, patient disclosure, mutation, message, notification, enrollment, +migration, deployment, pilot, or production activation was added or enabled. + +Pseudolocales are test transformations. They must never be described to a patient as a +translation. English fallback in an RTL layout is test evidence only and must not be used +as an Arabic release strategy. diff --git a/docs/nightingale/FOUNDATION-ACCESSIBILITY-LAYOUT-MATRIX-2026-07-26.md b/docs/nightingale/FOUNDATION-ACCESSIBILITY-LAYOUT-MATRIX-2026-07-26.md new file mode 100644 index 00000000..1dff9217 --- /dev/null +++ b/docs/nightingale/FOUNDATION-ACCESSIBILITY-LAYOUT-MATRIX-2026-07-26.md @@ -0,0 +1,537 @@ +# Nightingale foundation accessibility and layout matrix + +**Status:** implemented and locally verified for the bounded offline foundation; full +product accessibility conformance and independent human approval remain open + +**Decision date:** 2026-07-26 + +**Applies to:** the current Nightingale iOS and Android safe shell, privacy status card, +and device-local Display comfort card + +**Companion decision:** +[Presentation-preferences foundation decision](./PRESENTATION-PREFERENCES-FOUNDATION-DECISION-2026-07-26.md) + +**Does not authorize:** patient data, identity, networking, clinical content, account +preferences, production access, patient creation, pilot enrollment, deployment, or release + +## 1. Purpose + +The initial presentation-preferences slice proved reduced-motion and decorative-imagery +behavior, but it deliberately left a compound accessibility item open. This follow-up +tests and hardens the only currently executable patient surface at: + +- the largest iOS accessibility text category; +- Android font scale `2.0`; +- portrait and landscape; +- light and dark appearance; +- iOS Increased Contrast; +- ordered accessibility semantics; +- minimum interactive-target size; and +- measurable text-color contrast. + +This is a foundation-only matrix. It does not extrapolate from one offline screen to +future Today, My Path, Care Team, education, discharge, messaging, identity, recovery, or +representative journeys. + +## 2. Triggering finding + +A Release-screen audit on the iPhone 16e Simulator used: + +- dark appearance; +- Increased Contrast enabled; and +- `accessibility-extra-extra-extra-large`. + +The original iOS forest accent used fixed sRGB components `(0.20, 0.38, 0.29)` in both +appearances. Its contrast was: + +| Pairing | Ratio | Text threshold | Result | +| ----------------------- | ------- | -------------- | ------ | +| original forest / white | 7.129:1 | 4.5:1 | pass | +| original forest / black | 2.946:1 | 4.5:1 | fail | + +The patient-visible “Your privacy comes first” label was therefore too dark on the dark +system background. The defect was visible in the inspected simulator output and was not +closed by merely documenting an intended palette. + +The same audit found that: + +- iOS content did reflow and remain vertically scrollable at the largest text size; +- the status card remained reachable; +- Android had only a light Material color scheme and therefore did not explicitly honor + system dark appearance; and +- Android exposed the switch control, but the complete labeled preference row did not yet + have an explicit, testable 48 dp minimum. + +## 3. Bounded acceptance contract + +The current foundation is accepted only when all of the following are true: + +| Requirement | iOS acceptance | Android acceptance | +| -------------- | ------------------------------------------------------------------------------------------------------------------------------ | --------------------------------------------------------------------------- | +| Text growth | accessibility XXXL content exists and remains scroll-reachable | font scale `2.0` content exists and remains scroll-reachable | +| Landscape | both Display comfort controls can be reached and operated | both Display comfort rows can be reached and operated | +| Semantic order | product heading → privacy heading → Display comfort heading → reduce motion → hide imagery | same ordered test-tag traversal | +| Target size | each identified toggle has a frame height of at least 44 points | each complete labeled row has a height of at least 48 dp | +| Appearance | system light/dark resolution is explicit | `isSystemInDarkTheme()` selects an explicit light/dark scheme | +| Contrast | patient accent/system-background pair is at least 4.5:1 in light and dark | every defined patient text/container pair is at least 4.5:1 in both schemes | +| Images | exact seven-photo catalog stays absent from accessibility meaning, can be hidden, and uses the local Gregorian epoch-day index | same catalog, hashes, and index | +| Privacy | lifecycle cover remains operative | lifecycle cover and `FLAG_SECURE` remain operative | +| State cleanup | simulator settings are restored after evidence collection | font scale/orientation/night mode are restored after evidence collection | + +The 4.5:1 ratio is used as the bounded normal-text gate. Passing these palette pairs is not +a claim that every future component, state, overlay, image crop, disabled state, chart, or +clinical status meets WCAG contrast requirements. + +## 4. Implementation decisions + +### 4.1 iOS appearance-aware accent + +`NightingalePalette.forest` now wraps a dynamic `UIColor`: + +- light appearance: `(0.20, 0.38, 0.29)`; +- dark appearance: `(0.55, 0.80, 0.66)`. + +The accent resolves at runtime from the active `UITraitCollection`. The light value +preserves the existing calm forest identity. The dark value is a lighter mint-green that +retains that identity without becoming low-contrast against a black system surface. + +The status label and privacy-cover icon continue to consume this single semantic accent. +No clinical meaning is encoded by the color. + +### 4.2 Android light and dark schemes + +Android now defines two Nightingale-owned Material 3 schemes: + +| Token | Light | Dark | +| ---------------- | --------- | --------- | +| primary | `#365F49` | `#A8D5B8` | +| onPrimary | `#FFFFFF` | `#123323` | +| surface | `#FFFCF5` | `#17130F` | +| onSurface | `#24201B` | `#F7F0E7` | +| surfaceVariant | `#F1E9DC` | `#2B2723` | +| onSurfaceVariant | `#514A42` | `#D8CEC3` | + +`NightingaleFoundationScreen` defaults `darkTheme` from +`isSystemInDarkTheme()`. A pure `nightingaleColorScheme(darkTheme)` selector makes the +branch independently testable without platform screenshots. + +The privacy-status heading uses `primary`; body and supporting copy continue to use the +appropriate surface content tokens. Color remains decorative emphasis, not the sole +status cue. + +### 4.3 Full-row interaction targets + +iOS applies `frame(minHeight: 44)` and a rectangular content shape to both native toggles. +The native switch remains a switch; the change expands its row target without replacing +platform semantics. + +Android replaces separate label-plus-switch interaction with one `Row` that: + +- fills the card width; +- has `heightIn(min = 48.dp)`; +- uses `toggleable` with `Role.Switch`; +- leaves the visual `Switch` non-interactive so there is only one state-changing target; +- keeps the patient-readable label as a descendant of the checkable row; and +- wraps the label at large text rather than truncating it. + +The final UI Automator hierarchy exposes each full row as one enabled, focusable, +checkable, clickable element with its label beneath that semantic parent. + +### 4.4 Reading order + +Stable identifiers/test tags now bind the five required landmarks: + +1. `nightingale-product-heading` +2. `nightingale-privacy-status-heading` +3. `nightingale-display-comfort-heading` +4. `nightingale-reduce-motion-toggle` +5. `nightingale-hide-imagery-toggle` + +On iOS, the privacy label is combined into one accessibility element and the unnecessary +status-card containment boundary was removed. This corrected an observed XCUITest tree in +which the Display comfort card had been traversed before the privacy card even though the +visual source order was the reverse. + +On Android, the instrumentation suite recursively traverses the unmerged Compose semantics +tree and compares the ordered Nightingale tags. This avoids using clipped viewport +coordinates as a proxy for semantic order. + +### 4.5 Scroll and width behavior + +iOS retains a vertical `ScrollView` with a 520-point maximum content width. + +Android now: + +- aligns the scroll column at `TopCenter`; +- constrains it to a 520 dp maximum width; +- fills the available height; +- uses top rather than center vertical arrangement; and +- retains vertical scrolling for text growth and landscape height reduction. + +Top alignment prevents tall large-text content from depending on centering behavior as it +exceeds the viewport. + +### 4.6 Governed background catalog + +Both products package the exact same seven metadata-stripped derivatives from +`nightingale/backgrounds/optimized/drawable-nodpi`. The selection is stable for each local +Gregorian day and is identical across platforms: + +```text +floorMod(local Gregorian epoch day since 1970-01-01, 7) +``` + +The photo fills the viewport behind a strong system-surface gradient. Patient-readable +content remains on opaque or near-opaque cards, with full opacity under stronger supported +contrast/transparency settings. The complete background layer is hidden from accessibility +semantics and input. No filename, species, clinical state, pathway state, risk, or outcome +is exposed. + +The patient “Hide decorative imagery” setting removes the photo and decorative product +mark without removing text or controls. iOS also withholds the photo under Reduce +Transparency or Increased Contrast; Android withholds it under the supported high-contrast +signal. There is no carousel, parallax, timer-driven rotation, video, or automatic motion +loop. + +Exact source/derivative lineage, hashes, dimensions, metadata restrictions, release-rights +hold, and native packaging evidence are maintained in the +[background asset governance record](./BACKGROUND-ASSET-GOVERNANCE-AND-NATIVE-INTEGRATION-2026-07-26.md). + +## 5. Contrast proof + +### 5.1 Method + +Both native unit suites calculate relative luminance from sRGB components using the WCAG +piecewise linearization formula: + +```text +if c <= 0.04045: c_linear = c / 12.92 +else: c_linear = ((c + 0.055) / 1.055) ^ 2.4 + +L = 0.2126 R_linear + 0.7152 G_linear + 0.0722 B_linear +contrast = (L_lighter + 0.05) / (L_darker + 0.05) +``` + +iOS resolves the dynamic color against explicit light and dark trait collections before +calculating the ratio. Android tests both complete `ColorScheme` values through the +Compose `luminance()` implementation. + +### 5.2 Results + +| Platform | Foreground / background | Ratio | Gate | Result | +| ------------- | --------------------------------- | -------- | ---- | ------ | +| iOS light | forest / system background | 7.129:1 | 4.5 | pass | +| iOS dark | forest / system background | 11.324:1 | 4.5 | pass | +| Android light | primary / surfaceVariant | 6.031:1 | 4.5 | pass | +| Android light | onSurface / surface | 15.796:1 | 4.5 | pass | +| Android light | onSurfaceVariant / surfaceVariant | 7.238:1 | 4.5 | pass | +| Android light | onPrimary / primary | 7.267:1 | 4.5 | pass | +| Android dark | primary / surfaceVariant | 9.085:1 | 4.5 | pass | +| Android dark | onSurface / surface | 16.342:1 | 4.5 | pass | +| Android dark | onSurfaceVariant / surfaceVariant | 9.551:1 | 4.5 | pass | +| Android dark | onPrimary / primary | 8.451:1 | 4.5 | pass | + +These ratios are deterministic unit-test assertions, not manually sampled screenshot +colors. + +## 6. Native test matrix + +### 6.1 iOS + +The iOS Release contract now declares portrait, landscape-left, and landscape-right in +both `project.yml` and the generated application `Info.plist`. The new XCUITest: + +1. requests SwiftUI `DynamicTypeSize.accessibility5` through a compile-time + `#if DEBUG` launch-environment adapter; +2. rotates the simulator to landscape left; +3. launches with clean Nightingale presentation settings; +4. requires the application viewport width to exceed its height, proving the release + orientation contract produced an actual landscape layout; +5. requires the scalable product heading to measure at least 60 points high, proving the + requested accessibility category affected rendered geometry; +6. reads the accessibility-element sequence and compares all five required identifiers; +7. waits for the reduce-motion switch and asserts a frame height of at least 44 points; +8. operates the switch through XCTest's accessibility-aware auto-scroll and verifies its + enabled state; +9. repeats the existence, size, auto-scroll, and state checks for the imagery switch; and +10. restores portrait orientation in test teardown, including after a framework-level + interaction failure. + +The first attempted test used the UIKit `-UIPreferredContentSizeCategoryName` launch +argument. On the iOS 26.3.1 simulator, the heading remained `40.67` points high, which +proved that the argument did not affect the SwiftUI hierarchy in this environment. That +test correctly failed its 60-point geometry gate. The accepted test uses the +`NIGHTINGALE_TEST_ACCESSIBILITY_TEXT_SIZE` process environment key, which is read only +inside a compile-time `#if DEBUG` branch and applies `.dynamicTypeSize(.accessibility5)` +above the complete patient surface. Release compilation excludes the key and override. +The geometry assertion prevents a future no-op override from silently preserving a green +journey. + +The accepted full normally signed suite began from restored device settings: light +appearance, normal contrast, and system-large text. This makes the seven-page hierarchy +in the largest-text journey attributable to the Debug-only accessibility5 adapter instead +of leaked simulator state. The prior Release visual inspection was separately performed +with dark appearance, `DarkenSystemColors=1`, and +`UICTContentSizeCategoryAccessibilityExtraExtraExtraLarge`; it remains appearance and +reflow evidence, not landscape evidence. + +Results: + +| Suite | Tests | Failures | Skips | +| ---------------- | ----- | -------- | ----- | +| Nightingale unit | 9 | 0 | 0 | +| Nightingale UI | 4 | 0 | 0 | +| Total | 13 | 0 | 0 | + +The unit total includes the dynamic light/dark contrast test and the existing product, +protected-state, volatile-input, presentation-policy, preference-boundary, exact resource +catalog, and cross-platform daily-index tests. + +### 6.2 Android + +The new API 35 instrumentation journey: + +1. sets system font scale to `2.0` through instrumentation shell authority; +2. requests landscape orientation; +3. waits until both configuration changes are active; +4. traverses the unmerged semantics tree and compares the five ordered tags; +5. scrolls each complete preference row into view; +6. converts 48 dp to device pixels using the current density; +7. asserts each row meets or exceeds the threshold; +8. operates each row and verifies its checked state; and +9. restores font scale `1.0` and portrait orientation in `finally`. + +The final clean, no-build-cache run produced: + +| Suite/gate | Result | +| ----------------------------- | --------------------------------------------- | +| Debug JVM unit tests | 8 passed; 0 failed/skipped/errors | +| Release JVM unit tests | 8 passed; 0 failed/skipped/errors | +| API 35 instrumentation tests | 7 passed; 0 failed/skipped/errors | +| product-boundary task | passed | +| `lintDebug` / `lintRelease` | passed / passed | +| Debug assembly | passed | +| unsigned Release assembly | passed | +| Release APK boundary/hash set | passed; exact seven governed background JPEGs | + +The unit total includes pure light/dark scheme selection, all eight text-pair contrast +assertions, and exact background-catalog/daily-index coverage. + +## 7. Emulator and visual evidence + +### 7.1 iOS Release inspection + +The normally signed Release simulator application was installed cleanly on: + +- device: iPhone 16e Simulator; +- device ID: `3F568F29-BE58-49AD-8151-6C2303B4C4E3`; +- runtime: iOS 26.3.1; +- appearance: dark; +- contrast: Increased Contrast; +- text: accessibility XXXL. + +Visual inspection confirmed: + +- text reflows rather than truncating; +- the page scrolls vertically; +- the status card begins after the introductory copy; +- the corrected accent is visibly distinct against the black card; +- card boundaries remain visible; +- decorative imagery is withheld by the stronger contrast policy; and +- no essential content depends on the bird image. + +The local Release screenshot SHA-256 is +`f3ca5fb5eb5281424d08858e56fdd381eb06ed5fa47d607dfc1bfc480b08f153`. +The screenshot is local diagnostic evidence, not a distribution screenshot or an +independent patient review. + +An additional current foundation inspection at normal contrast exercised the real +seven-photo catalog. The 1170×2532 screenshot showed a supplied photo behind a strong +system-surface gradient, readable near-opaque cards, no text directly on raw photography, +and the restrained Nightingale mark. Its SHA-256 is +`07494f15eadc37916613fd502cc43a8ccaffc7ce2a31a1b1c9d9a51abe208272`. +This remains local diagnostic evidence; it is not patient-advisor, accessibility, rights, +marketing, store, or release approval. + +### 7.2 Android hierarchy inspection + +The `hb` Android 15/API 35 AVD was booted without a saved-state dependency. A manual +hierarchy inspection used dark appearance and font scale `2.0`. + +At 420 dpi, both final portrait preference rows measured 222 pixels high, approximately +84.6 dp, because their labels wrapped at the enlarged font scale. The instrumentation +journey independently proves each landscape row remains at least 48 dp. + +The final UI Automator XML SHA-256 is +`6392d3ef5ebfbcd4ef85b568e94c067b90fb77725ef4197e85f7a82518d13fcf`. + +Android continues to apply `FLAG_SECURE`. External screenshots therefore contain system +chrome and a black application surface. The final secure-capture SHA-256 is +`5d5c731e9047f2c26407fab4bfa66b40ae59788da3438089db5f479732b53ebc`. +That black capture is evidence that capture protection remains active; it is not accepted +as dark-theme visual evidence. Human dark-theme review on an approved physical or secure +review device remains open. + +## 8. Exact local artifact binding + +| Artifact | SHA-256 | +| -------------------------------- | ------------------------------------------------------------------ | +| iOS Debug simulator executable | `4cd5c9bf61a0641115e8f48fd1ed271983a5c0cd137e89bbad6128a136f998db` | +| iOS Release simulator executable | `ddf102782eec071371e2537f14ec8b41a4afcf2fef12b89d1d0a603cf5495c2b` | +| iOS Release application manifest | `cc49573008857a7a658978b871553c922bf928577a80a7cece3750e804f6ef0c` | +| Android Debug APK | `729a1263e19d9097a6588ccd4a4adde5bcb176892659aaf8a6d6f3c0d5adac36` | +| Android unsigned Release APK | `3210ca5b232938c459ccb7a01d84df3c8fd8290ff63ce131d503cc1820860e38` | + +The iOS manifest is the built Release `Nightingale.app/Info.plist`; it contains portrait, +landscape-left, and landscape-right. These are local simulator/emulator artifacts. They +are not App Store/Play artifacts, distribution signatures, production releases, or pilot +approval. + +## 9. Mechanical enforcement + +`verify-nightingale-product-boundary.sh` now fails if any of these bounded safeguards is +removed: + +- the governed seven-file background verifier fails or is bypassed; +- iOS and Android stop packaging the exact shared derivative set; +- the cross-platform local-Gregorian-epoch-day selection rule changes; +- imagery becomes accessible, essential, state-bearing, autonomous motion, or direct text + background; +- the iOS accent stops resolving by appearance; +- the iOS 44-point row target disappears; +- the iOS light/dark contrast test disappears; +- the iOS largest-text landscape journey disappears; +- either iOS landscape orientation disappears from the project or application manifest; +- the iOS accessibility-size launch adapter escapes its Debug-only branch; +- the Android dark color scheme disappears; +- the Android screen stops following system dark appearance; +- the Android 48 dp row target disappears; +- the Android light/dark contrast test disappears; or +- the Android largest-text landscape journey disappears. + +The existing verifier continues to enforce no networking, no Android `INTERNET` +permission, correct application identities, exact presentation namespaces, Debug-only +test reset, and no account/cloud preference store. + +## 10. Failure history + +Preliminary failures are retained because they changed the implementation: + +1. The original iOS fixed accent failed the dark-background text gate at 2.946:1. +2. The first iOS semantic-order test exposed Display comfort before the privacy status + because the status card created an unnecessary contained accessibility subtree. +3. An unsigned iOS unit-test run produced Keychain status `-34018`; the normally signed + full run passed the protected-state canary and is the accepted result. +4. The first Android compilation imported the receiver-scoped `weight` member directly; + removing the invalid import allowed the `RowScope` call to compile. +5. The first Android order assertion compared clipped viewport coordinates. Offscreen + nodes reported zero-valued clipped bounds, so the assertion was replaced with actual + semantics-tree traversal. +6. The first attempted semantics helper used an unavailable `getOrNull` API. It was + replaced with explicit configuration-key membership and indexed retrieval. +7. Android screenshots remained black because `FLAG_SECURE` is functioning. No bypass was + added for audit convenience. +8. The first iOS largest-text journey used the UIKit preferred-content-size launch + argument, but the iOS 26.3.1 SwiftUI hierarchy remained at a `40.67`-point product + heading and correctly failed the 60-point geometry gate. The accepted Debug-only + environment adapter produced the seven-page accessibility hierarchy and passed the + complete journey. +9. A post-publication threat-surface inventory found that the test rotated the simulator + but the Release `Info.plist` declared portrait only. The orientation contract was + expanded to both landscape directions, and the journey now fails unless the application + viewport itself is wider than it is tall. Earlier test success is not used as proof of + distributed landscape support. +10. The first truly landscape run expanded to seven pages and exposed that probing + `XCUIElement.isHittable` while a control was still fully offscreen can itself raise an + invalid-activation-point failure. A center-in-viewport attempt admitted a partially + clipped frame beginning at y = -4 points. A full-frame-in-viewport attempt then exposed + XCTest's rotated-coordinate mismatch. The accepted proof checks existence and 44-point + frame height, performs XCTest's accessibility-aware auto-scroll/tap, and requires the + state change. Those failures also showed that a method-local `defer` did not reset + orientation after a framework-level interaction failure, so portrait restoration now + runs from XCTest teardown. + +None of these preliminary failures is counted as passing evidence. + +## 11. What is complete + +### 11.1 2026-07-27 language-readiness addendum + +The follow-up +[foundation accessibility and language-readiness record](./FOUNDATION-ACCESSIBILITY-LANGUAGE-READINESS-2026-07-27.md) +closes the prior matrix's **bounded current-shell** pseudo-localization and RTL exercises. +It adds exact 15-string cross-platform catalogs, three headings, restrained preference +status announcements, iOS rendered double-length and RTL UI journeys, Android Debug +`en-XA`/`ar-XB` instrumentation, retained visual/hierarchy evidence, and English-only +Release-artifact checks. + +This addendum does not revise the prior artifact hashes below; the follow-up record binds +its own rebuilt artifacts. It also does not close human assistive-technology review, +product-wide accessibility, non-English patient copy, or any future clinical screen. + +The following bounded foundation subset is complete: + +- light/dark patient text palette selection; +- automated contrast ratios for every currently declared semantic text pair; +- largest text/font-scale reflow and scroll reachability; +- landscape reachability of both current controls; +- ordered foundation landmarks and controls; +- 44-point iOS and 48 dp Android interaction targets; +- full-row Android switch interaction; +- exact shared seven-photo packaging and deterministic daily selection; +- metadata/hash/dimension enforcement and Release artifact asset-set scans; +- imagery-hidden, stronger-contrast/transparency, semantics, and no-motion boundaries; +- iOS Increased Contrast Release inspection; +- Android dark configuration plus secure semantic hierarchy inspection; and +- exact local Debug/Release artifact binding; +- exact cross-platform English source-copy reconciliation; +- current-shell rendered text-expansion and RTL exercises; and +- current-shell heading and restrained status-announcement policy. + +## 12. What remains open + +The following are intentionally not closed: + +- WCAG 2.2 AA conformance for the product; +- VoiceOver and TalkBack manual traversal by an independent reviewer; +- focus recovery after errors, modals, navigation, live updates, and authentication; +- keyboard, Switch Control, Voice Control, external-input, and magnification journeys; +- product-wide right-to-left behavior beyond the current offline shell; +- pseudo-localization and language-expansion testing for every future screen and state; +- approved non-English patient copy, translation QA, locale fallback, and interpreter + workflows; +- captions, transcripts, audio, media controls, charts, tables, and clinical status + semantics because none exists in the foundation; +- disabled, destructive, urgent, warning, stale, correction, and retraction states because + none is approved for rendering; +- physical-device visual review; +- durable license/permission/attribution evidence for all seven backgrounds; +- patient-advisor review of image comfort, crop, cultural interpretation, and the + images-hidden experience; +- iOS backup/restore behavior for local presentation choices; +- every future screen and every future color/state pair; +- distribution-signing and store-artifact evidence; and +- named patient-advisor, accessibility, privacy/security, clinical, content, language, + legal/HIM, nursing, medical-staff, pharmacy, support, and release approvals. + +The compound Stream E conformance item therefore remains open. + +## 13. Rollback + +If this slice must be rolled back: + +1. revert the dynamic iOS accent and Android dark scheme together with their tests; +2. revert only the foundation layout/semantic target changes; +3. retain the existing lifecycle privacy cover, `FLAG_SECURE`, no-network boundary, + presentation preference storage, reduced-motion behavior, and imagery suppression; and +4. do not clear care-account, identity, protected clinical, or patient state. + +No such patient state exists in this foundation. + +## 14. Safety statement + +No production database or source was accessed. No patient, principal, identity link, +grant, session, encounter, projection, preference, message, pathway, clinical release, +feature flag, migration, deployment, or pilot state was created, read, or changed. + +The result remains an offline, no-patient-data Nightingale foundation. diff --git a/docs/nightingale/FOUNDATION-DEPENDENCY-INVENTORY-2026-07-26.md b/docs/nightingale/FOUNDATION-DEPENDENCY-INVENTORY-2026-07-26.md new file mode 100644 index 00000000..2bafd1ef --- /dev/null +++ b/docs/nightingale/FOUNDATION-DEPENDENCY-INVENTORY-2026-07-26.md @@ -0,0 +1,317 @@ +# Nightingale foundation dependency inventory + +**Status:** Generated, source-hash-bound engineering inventory for the offline native +foundation. This is not a CycloneDX or SPDX conformance claim, license determination, +vulnerability assessment, software-supply-chain approval, signed-artifact manifest, or +authorization for distribution or live clinical use. + +**Canonical machine record:** +[`supply-chain/foundation-dependency-inventory.v0.json`](./supply-chain/foundation-dependency-inventory.v0.json) + +**Governing plan:** +[`nightingale-patient-product-2026-07-26.md`](../plans/nightingale-patient-product-2026-07-26.md) + +## 1. Executive disposition + +| Question | Verified foundation answer | +| ------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------- | +| What Android runtime graph is bounded? | `releaseRuntimeClasspath` for `nightingale/androidApp/app` | +| How many Android direct runtime declarations exist? | 7 | +| How many unique external Android Release runtime components resolve? | 83 | +| How many unique resolved Android dependency edges are recorded? | 457 | +| Does the iOS application target declare a third-party package manager? | No | +| How many iOS third-party runtime packages are declared? | 0 | +| Which Apple modules does the iOS application source import? | `Combine`, `Foundation`, `Security`, `SwiftUI`, and `UIKit` | +| Did generation require a patient, credential, database, API, or network? | No patient/production system was accessed; Gradle resolved public build dependency metadata | +| Is the record deterministic? | Yes; timestamps are omitted and six source files are bound by SHA-256 | +| Is the record continuously vulnerability-scanned or provenance-approved? | No | +| Does this approve the native apps for signing, stores, pilot, or use? | No | + +The inventory closes the missing **generated dependency-inventory evidence** for the +current Stream A foundation. It does not close the independent signing, store, +vulnerability, provenance, license, penetration-test, clinical-safety, privacy, identity, +source, patient-advisor, accessibility, support, pilot, or release gates. + +## 2. Why this record exists + +The initial Nightingale product plan required a generated +“software-bill-of-materials/dependency inventory” before the native foundation could leave +its product-foundation workstream. A hand-written package list would not be sufficient +because Android version alignment and transitive resolution change the runtime graph beyond +the seven declarations visible in `dependencies {}`. Conversely, describing the current +iOS target as dependency-free without bounding what was inspected would overstate the +finding. + +This record therefore separates four different facts: + +1. **Declared dependencies:** what the source explicitly requests. +2. **Resolved runtime components:** which external Android module versions Gradle selects + for the Release runtime classpath. +3. **Resolved graph edges:** which component/request relationships produce that selected + graph. +4. **Observed iOS package-manager state:** whether the XcodeGen source or common + package-manager files declare third-party packages, plus the Apple modules imported by + the application source. + +Gradle documents `ResolutionResult` as the model containing selected component instances +and their relationships in the resolved graph: +[Gradle `ResolutionResult`](https://docs.gradle.org/current/kotlin-dsl/gradle/org.gradle.api.artifacts.result/-resolution-result/index.html). +The generator uses that model rather than scraping human-formatted console output. + +Apple documents Swift packages as reusable components that Xcode can add and manage as +package dependencies: +[Apple Swift packages](https://developer.apple.com/documentation/xcode/swift-packages). +The current Nightingale XcodeGen source declares none. That does **not** turn Apple SDK +modules into third-party packages or inventory the contents of iOS itself. + +## 3. Exact scope + +### 3.1 Included + +The machine record includes: + +- the seven direct dependencies inherited by Android `releaseRuntimeClasspath`; +- all unique external module components selected by Gradle for that configuration; +- every unique resolved `from` → `requested` → `selected` relationship reported by Gradle; +- declared Gradle wrapper, Android Gradle Plugin, Kotlin plugin, Java, and Android SDK + requirements; +- the XcodeGen source path, Swift language version, and minimum iOS version; +- the absence of Swift Package Manager, CocoaPods, and Carthage source inputs in the + bounded iOS root; +- the absence of an XcodeGen `packages:` or target `package:` declaration; +- Apple module imports found in the `Nightingale` application-target Swift sources; and +- SHA-256 values for all six source inputs that define or generate the record. + +### 3.2 Excluded + +This inventory deliberately does not claim to include or determine: + +- Android Debug, unit-test, instrumentation-test, lint, annotation-processor, or other + configurations; +- resolved Gradle plugin/build-tool transitive classpaths; +- downloaded artifact bytes, artifact SHA-256 values, PGP signatures, repository identity, + or reproducible-build equivalence; +- iOS SDK, operating-system, compiler, Xcode, linker, or Apple-framework component + contents; +- embedded binary/framework inspection of a signed iOS archive or Android app bundle; +- license identity, license compatibility, notices, export-control status, or legal advice; +- vulnerability, malware, exploitability, end-of-life, or threat-intelligence findings; +- signing certificates, provisioning profiles, entitlements, store records, release + manifests, notarization, or provenance attestations; +- build runner, host OS, container/image, package registry, source repository, or account + security; +- clinical safety, patient privacy, authorization, identity, data-source, messaging, + support, accessibility, language, content, pilot, or operational readiness; or +- CycloneDX, SPDX, SLSA, OWASP MASVS, NIST, regulatory, or other standards compliance. + +These exclusions are intentional. A bounded and accurate inventory is useful evidence; an +overbroad “SBOM complete” label would create false assurance. + +## 4. Source-of-truth boundary + +The generator binds the canonical record to exactly these files: + +| Source | Role | +| --------------------------------------------------------------------- | ----------------------------------------------------------------------------- | +| `nightingale/androidApp/app/build.gradle.kts` | Android app configuration, direct dependencies, and resolution-report task | +| `nightingale/androidApp/build.gradle.kts` | Android Gradle Plugin and Kotlin plugin declarations | +| `nightingale/androidApp/settings.gradle.kts` | Plugin and dependency repository declarations | +| `nightingale/androidApp/gradle/wrapper/gradle-wrapper.properties` | Gradle wrapper version | +| `nightingale/iosApp/project.yml` | iOS application target, deployment target, build settings, and package source | +| `scripts/ci/generate-nightingale-foundation-dependency-inventory.mjs` | Deterministic assembly and scope logic | + +The JSON stores the SHA-256 of each file. The verifier recalculates every value from the +checkout. A dependency declaration, plugin version, repository, wrapper version, SDK +requirement, XcodeGen source, or generator change makes the checked-in record stale until +it is regenerated and reviewed. + +The verifier itself is intentionally not part of that hash set. Its behavior is exercised +by negative mutations in CI; changing verification policy does not rewrite the dependency +record unless an inventoried source also changes. + +## 5. Android resolution method + +### 5.1 Source declarations + +The current Release runtime inherits seven direct declarations: + +| Declaration | Requested version source | +| ------------------------------------------- | ------------------------ | +| `androidx.activity:activity-compose` | Explicit `1.10.0` | +| `androidx.compose:compose-bom` | Explicit `2025.02.00` | +| `androidx.compose.animation:animation-core` | Compose BOM managed | +| `androidx.compose.foundation:foundation` | Compose BOM managed | +| `androidx.compose.material3:material3` | Compose BOM managed | +| `androidx.compose.ui:ui` | Compose BOM managed | +| `androidx.compose.ui:ui-tooling-preview` | Compose BOM managed | + +Test and Debug declarations are excluded because the recorded configuration is specifically +the Release runtime. They remain build inputs that require a future broader build-supply- +chain inventory. + +### 5.2 Resolution task + +The app Gradle file defines +`:app:writeNightingaleReleaseDependencyResolution`. It: + +1. selects `releaseRuntimeClasspath`; +2. queries `configuration.incoming.resolutionResult`; +3. records all external `ModuleComponentIdentifier` values; +4. records every successfully resolved dependency relationship; +5. removes duplicate relationship triples; +6. sorts declarations, components, and edges by stable tuple keys; and +7. writes a structured intermediate JSON report under the ignored Gradle build directory. + +The repository generator reads the structured report and embeds it in the canonical +cross-platform record. It does not parse Gradle console decoration, tree glyphs, conflict +markers, or log wording. + +### 5.3 Interpretation of counts + +The 83-component result does not mean product owners selected 83 first-order libraries. It +contains: + +- direct runtime libraries; +- version-alignment platforms/BOMs represented in the resolution graph; +- transitive AndroidX modules; +- Kotlin runtime variants; +- coroutine modules; and +- other transitive modules required by the selected graph. + +The 457 edges are deduplicated graph relationships, not 457 unique packages. Components and +edges serve different reconciliation purposes and must not be conflated. + +## 6. iOS inspection method + +The current iOS application uses XcodeGen source-of-truth configuration. The generator: + +1. inspects `nightingale/iosApp/project.yml`; +2. rejects an XcodeGen `packages:` block; +3. rejects a target-level `package:` dependency; +4. rejects `Package.swift`, `Package.resolved`, `Podfile`, `Podfile.lock`, `Cartfile`, or + `Cartfile.resolved` within the Nightingale iOS root; +5. scans only the application target’s `.swift` files for `import` statements; and +6. records the four current Apple modules. + +“Zero iOS third-party runtime packages” therefore means no third-party package-manager +input exists in this bounded source model. It does not mean the compiled app contains zero +Apple framework dependencies, nor does it inspect a signed archive for embedded +third-party binaries. + +If Swift Package Manager is later introduced, the inventory design must be extended to +record and verify the checked-in resolved package graph. Apple’s CI guidance explains that +`Package.resolved` stores exact package versions: +[Apple package dependencies in CI](https://developer.apple.com/documentation/xcode/building-swift-packages-or-apps-that-use-them-in-continuous-integration-workflows). + +## 7. Generation and verification + +### 7.1 Regenerate + +Use a JDK 17 runtime: + +```bash +JAVA_HOME= \ + node scripts/ci/generate-nightingale-foundation-dependency-inventory.mjs . +``` + +The generator runs Gradle, replaces the canonical JSON, and prints the component summary. +Review the complete JSON delta. Do not commit a regenerated record solely because the +command succeeds. + +### 7.2 Verify without resolving dependencies + +```bash +node scripts/ci/verify-nightingale-foundation-dependency-inventory.mjs . --self-test +``` + +The dependency-free verifier checks: + +- exact product identity and offline/unapproved release state; +- exact inventory scope and limitation statements; +- exact source-hash key set and current SHA-256 values; +- exact seven direct Android declarations; +- non-empty, unique, deterministically sorted Android component and edge sets; +- required selected component versions; +- referential integrity for every dependency edge; +- internal count reconciliation; +- declared Android/iOS build requirements; +- zero iOS third-party packages and exactly four current Apple imports; +- absence of unrecorded package-manager inputs; +- absence of patient-data, production-access, standards, and live-use claims; and +- non-authorization language. + +### 7.3 Negative self-tests + +Nine isolated mutations prove that the verifier rejects: + +1. a patient-data claim; +2. a production-access claim; +3. a CycloneDX conformance overclaim; +4. a stale source hash; +5. a removed direct Android dependency; +6. a duplicate resolved component; +7. an edge pointing to an unrecorded component; +8. a newly asserted iOS third-party package; and +9. a live-release approval claim. + +This is mutation evidence for the verifier’s bounded rules. It is not adversarial +supply-chain testing. + +## 8. Supply-chain risk disposition + +The generated inventory improves the foundation’s ability to detect and review dependency +drift, but it does not close `THR-SC-001` or `RISK-007` in the threat/hazard model. + +In particular, Gradle dependency verification is a separate control. Gradle’s official +guidance states that verification metadata can record checksums/signatures and also warns +that bootstrapping metadata trusts what is present at generation time: +[Gradle dependency verification](https://docs.gradle.org/current/userguide/dependency_verification.html). +No such metadata or trust ceremony is claimed by this work. + +Required future supply-chain work includes: + +- approved dependency ownership and update cadence; +- artifact checksum/signature verification with independent bootstrap review; +- registry/repository allowlisting and provenance policy; +- build-plugin and CI action inventories; +- continuous vulnerability intake, exploitability assessment, response SLOs, and + emergency update procedure; +- license/notice review; +- signed iOS archive and Android app-bundle embedded-component inspection; +- reproducibility or independent artifact-verification strategy; +- signing-key, store-account, runner, and release-manifest security; +- unsupported-version and forced-upgrade policy; and +- incident exercises for a compromised dependency, registry, build runner, signing key, + or store account. + +## 9. Change-control rules + +Regenerate, review, and obtain fresh exact-SHA CI whenever any of these change: + +- a direct Android dependency or dependency configuration; +- a Gradle resolution rule, BOM, repository, plugin, wrapper, Java, or SDK version; +- the resolution-report task; +- the iOS XcodeGen target, package declaration, deployment target, or Swift version; +- an iOS package-manager input or application-target import; +- the generator’s scope or interpretation rules; or +- the product identifier or release-state assertion. + +Adding a dependency must not be interpreted as approval to add networking, patient data, +identity, clinical content, messaging, analytics, crash reporting, notifications, or any +other capability. Those changes require their own threat/hazard revision, privacy and +clinical review, contract/authorization evidence, native tests, and release gates. + +## 10. Non-authorization statement + +This inventory: + +- used no patient, patient record, production database, production API, identity, grant, + session, credential, or private fixture; +- adds no route, network permission, network client, source adapter, disclosure, mutation, + message, notification, analytics, or crash SDK; +- does not approve any dependency, version, license, repository, vendor, or risk; +- does not assert that the graph is vulnerability-free, tamper-free, reproducible, signed, + complete for all build configurations, or standards-conformant; +- does not authorize distribution, deployment, migration, pilot, or clinical use; and +- does not replace security, privacy, legal, clinical, accessibility, language, support, + patient-advisor, release, or operational review. diff --git a/docs/nightingale/FOUNDATION-PRIVACY-RELEASE-CONTROLS-2026-07-26.md b/docs/nightingale/FOUNDATION-PRIVACY-RELEASE-CONTROLS-2026-07-26.md new file mode 100644 index 00000000..42d20c42 --- /dev/null +++ b/docs/nightingale/FOUNDATION-PRIVACY-RELEASE-CONTROLS-2026-07-26.md @@ -0,0 +1,193 @@ +# Nightingale foundation privacy and release-control evidence + +**Status:** Implemented engineering evidence for the offline foundation only. This is not +a privacy approval, App Store or Play declaration, mobile penetration test, patient-data +authorization, release authorization, or claim that a future networked product collects no +data. + +**Decision date:** 2026-07-26 + +**Applies to:** the exact Nightingale iOS and Android offline foundation source and Release +artifacts on `codex/nightingale-patient-product`. + +## 1. Outcome + +The bounded privacy controls inherited as design evidence from Hummingbird Patient have +now been independently reissued, tightened, and rerun under Nightingale product +identities and assets: + +- the iOS application carries one exact `PrivacyInfo.xcprivacy` file; +- that manifest declares no tracking, no collected-data types in the current offline + foundation, and only the app-local `UserDefaults` required-reason API use; +- Android continues to request no `INTERNET` permission and now also has explicit + cleartext denial, system-only trust anchors, and no debug trust override; +- Android backup and device transfer remain disabled and excluded; +- iOS lifecycle covering and Android lifecycle covering plus `FLAG_SECURE` remain + independently tested; +- dormant protected storage and volatile-input deletion remain product-namespaced and + fail closed; +- runtime sources contain no logging, analytics, crash-reporting, clipboard, pasteboard, + or network-client implementation; and +- both Release artifact verifiers inspect the installed declarations, not only the source + files. + +These controls close the plan item to carry forward and rerun the existing automated +privacy controls **as candidate evidence**. They do not approve patient access, identity, +networking, telemetry, messaging, notification, clinical content, production data, or +distribution. + +## 2. Primary platform requirements used + +The implementation was checked against current primary platform documentation: + +- Apple requires every executable using a required-reason API to include that API and an + approved reason in its privacy manifest: + [Describing use of required reason API](https://developer.apple.com/documentation/bundleresources/describing-use-of-required-reason-api). +- Apple's app-local `UserDefaults` reason is `CA92.1`: + [NSPrivacyAccessedAPITypeReasons](https://developer.apple.com/documentation/bundleresources/app-privacy-configuration/nsprivacyaccessedapitypes/nsprivacyaccessedapitypereasons). +- Android's Network Security Configuration supports declarative cleartext opt-out and + trust-anchor policy: + [Network security configuration](https://developer.android.com/privacy-and-security/security-config). +- Android documents `android:usesCleartextTraffic` as a defense honored by many platform + network components while noting that it is not a universal socket-level control: + [application manifest element](https://developer.android.com/guide/topics/manifest/application-element). + +The repositories and generated binaries remain the authoritative evidence for what this +foundation actually implements. + +## 3. iOS privacy manifest + +The application source contains exactly one privacy manifest at +`nightingale/iosApp/Nightingale/PrivacyInfo.xcprivacy`. + +| Manifest key | Exact foundation value | Reason and boundary | +| ---------------------------------- | ------------------------ | ------------------------------------------------------------------------------------------------------------------------ | +| `NSPrivacyTracking` | `false` | The offline foundation contains no tracking implementation. This is not a promise about an unreviewed future SDK/client. | +| `NSPrivacyTrackingDomains` | Absent | No tracking domain is permitted or needed. | +| `NSPrivacyCollectedDataTypes` | Empty array | The current application has no patient identity, network, telemetry, or data-collection path. | +| `NSPrivacyAccessedAPITypes` | One `UserDefaults` entry | The app persists only two local display-comfort choices. | +| `NSPrivacyAccessedAPITypeReasons` | `CA92.1` | Those preferences are accessible only to Nightingale itself; no app group or cross-app access exists. | +| Additional accessed-API categories | None | No other required-reason API use was identified in the bounded application source. | + +The unit suite loads the manifest from `Bundle.main`, proving that the generated test host +actually packages it. The Release verifier recursively inventories `.xcprivacy` files, +requires only `PrivacyInfo.xcprivacy`, parses it as a property list, and compares the +complete dictionary to the approved offline-foundation declaration. + +This is local manifest evidence only. App Store Connect privacy answers, nutrition labels, +third-party SDK declarations, signed archive validation, data-retention policy, and a +privacy notice remain release gates. + +## 4. Android transport and backup defense in depth + +The Android application manifest keeps these independent controls: + +| Control | Exact implementation | Verification | +| ---------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------- | +| No network capability | No `android.permission.INTERNET` | Gradle boundary task, installed instrumentation, and APK permission inventory | +| Explicit cleartext denial | `android:usesCleartextTraffic="false"` | Source verifier, installed `NetworkSecurityPolicy`, and compiled manifest inspection | +| Declarative network policy | `android:networkSecurityConfig="@xml/network_security_config"` | Source verifier, Gradle boundary task, and compiled resource inventory | +| Trust anchors | System certificate authorities only | Exact XML check; no user-added CA and no debug override | +| Application backup | `android:allowBackup="false"` | Source, installed `ApplicationInfo.FLAG_ALLOW_BACKUP`, and APK manifest | +| Cloud backup and device transfer | Full exclusions in `backup_rules.xml` and `data_extraction_rules.xml` | Gradle boundary task and compiled manifest/resource checks | +| Ordinary screenshot/recording protection | `WindowManager.LayoutParams.FLAG_SECURE` | Unit constant check, installed-window instrumentation, and black `adb screencap` evidence | +| Background/task-switcher content cover | `MainActivity.onPause` applies the privacy cover and clears volatile input; `onResume` removes the cover only for active use | Lifecycle instrumentation | + +The app still has no network stack. The network-security configuration is defense in depth +against an accidental future platform client; it is not permission to add a host, API +route, certificate pin, `INTERNET` permission, or transport implementation. + +## 5. Cross-platform privacy-control reconciliation + +| Reference concern | Nightingale iOS evidence | Nightingale Android evidence | Current limitation | +| ------------------------------- | ------------------------------------------------------------------------------------------ | --------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------- | +| Product isolation | `net.acumenus.nightingale`; Nightingale-only Keychain and preference keys | `net.acumenus.nightingale`; Nightingale-only Keystore and preference keys | No independent live identity realm/provider exists | +| Network absence | No client imports/tokens in source or Release executable | No `INTERNET`; no client; explicit cleartext denial and system-only trust | Future TLS, endpoint, certificate, and outage policy remains unapproved | +| Required privacy declaration | Exact packaged privacy manifest | Manifest/resource declarations inspected in APK | Store-console declarations and signed distribution remain absent | +| Background privacy | Cover whenever scene is inactive; covered content removed from accessibility/hit testing | Cover on pause and `FLAG_SECURE`; volatile input clears | iOS active-screen capture remains an explicit open decision before PHI | +| Protected local state | Non-synchronizable, data-protection Keychain, `WhenUnlockedThisDeviceOnly` | AES-256-GCM Android Keystore primitive with tamper failure and verified deletion | Dormant synthetic binding only; no real session format or user-authentication policy | +| Backup/transfer | Protected binding is this-device-only; preferences are nonclinical | App backup false plus complete cloud/transfer exclusions | Signed-artifact backup/restore/upgrade exercises remain open | +| Volatile patient-entered text | In-memory only and cleared at six sensitive boundaries | In-memory only and cleared at the same six boundaries | Immutable Swift/Kotlin strings cannot be reliably zeroized; no approved input/keyboard/clipboard policy | +| Logging, analytics, crash, copy | Mechanically absent from bounded runtime source | Mechanically absent from bounded runtime source | A future observability design requires a privacy-minimized event model and vendor review | +| Release-only test escape | Release executable rejects Debug hook tokens and unapproved embedded frameworks/extensions | Release APK rejects Debug/test state, endpoints, extra permissions/components, and unapproved library inventory | Distribution signing, notarized/store processing, and tamper testing remain open | + +The older Hummingbird Patient implementation remains evidence only. No legacy source file +was copied to claim parity, and no legacy identifier, endpoint, token, or production +configuration was introduced. + +## 6. Mechanical enforcement + +The following checks fail closed: + +1. `scripts/ci/verify-nightingale-product-boundary.sh` + - requires and exactly parses the iOS privacy manifest; + - requires Android cleartext denial, network-security resource, system trust anchors, + and no debug override; + - rejects any Android `INTERNET` permission; + - rejects native network-client types; + - rejects runtime logging, analytics, crash-reporting, clipboard, or pasteboard APIs; + - retains namespace, accessibility, background, backup, and Release-hook controls. +2. `nightingale/androidApp/app/build.gradle.kts` + - repeats the Android source/resource policy inside the Gradle `check` lifecycle. +3. `NightingaleProductBoundaryTests.swift` + - reads `PrivacyInfo.xcprivacy` from the built app and checks the complete exact + dictionary. +4. `NightingaleLaunchInstrumentedTest.kt` + - checks the installed package identity; + - proves `INTERNET` is denied; + - proves platform cleartext policy is false; and + - proves the installed application does not carry `FLAG_ALLOW_BACKUP`. +5. Release artifact verifiers + - iOS requires the exact packaged privacy manifest and continues executable/linkage/ + resource scans; + - Android requires the compiled transport declarations and continues permission, + component, DEX, library, and resource scans. + +## 7. Native verification protocol + +The accepted local evidence for this slice must include: + +### iOS + +1. regenerate the Xcode project and prove no project drift; +2. build a signed Debug test host for the selected iPhone Simulator; +3. run the complete XCTest suite, including packaged-manifest validation; +4. run all XCUITest shell/privacy/accessibility journeys; +5. build the unsigned Release application; +6. verify the exact Release application bundle; and +7. inspect the inactive privacy cover and active safe shell on the simulator. + +### Android + +1. run Debug and Release unit tests; +2. run Debug and Release lint; +3. assemble Debug and unsigned Release APKs; +4. verify the exact Release APK; +5. run the complete instrumentation suite on API 35, including installed privacy-policy + checks; +6. confirm a shell-level `adb screencap` of the active secure window remains black; and +7. inspect the UI hierarchy to confirm no sensitive, legacy, source, or background-file + semantics appear. + +Exact commands, simulator/emulator identifiers, test counts, hashes, and exact-SHA CI are +recorded in the companion execution log after the run completes. + +## 8. Deliberate holds and residual risk + +This evidence does not authorize: + +- a patient, principal, encounter, grant, session, sample patient, or production query; +- a network permission, client, endpoint, route, identity provider, or certificate pin; +- clinical content, communication, push, telemetry, crash reporting, or support tooling; +- App Store/Play privacy declarations or an assertion that a future product collects no + data; +- active-screen capture behavior on iOS once PHI exists; +- patient input, clipboard, keyboard, autofill, background refresh, notification, widget, + extension, or sharing behavior; +- distribution signing, store processing, penetration testing, or pilot activation; or +- privacy, security, legal, clinical, accessibility, or release approval. + +The current control is safe because the application is offline, contains no patient data, +and mechanically withholds every live capability. Any future operation changes the data +flow and invalidates the no-collected-data assumption until the manifest, store +declarations, contracts, threat model, tests, and named approvals are reviewed together. diff --git a/docs/nightingale/FOUNDATION-THREAT-AND-HAZARD-MODEL-2026-07-26.md b/docs/nightingale/FOUNDATION-THREAT-AND-HAZARD-MODEL-2026-07-26.md new file mode 100644 index 00000000..f6cad7f6 --- /dev/null +++ b/docs/nightingale/FOUNDATION-THREAT-AND-HAZARD-MODEL-2026-07-26.md @@ -0,0 +1,631 @@ +# Nightingale foundation threat and clinical-hazard model + +| Field | Value | +| --------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| Document status | **Draft engineering model; not an approved safety case or release authorization** | +| Assessment date | 2026-07-26 | +| Executable baseline | `80c29e9ff5a745e50e7efdca4c2ac9c3a3df8091` | +| Product | Nightingale, the independent inpatient-facing product | +| Current exposure | Offline native foundation; no live patient access, network client, identity, patient route, source query, disclosure, mutation, messaging, notification, or production activation | +| Intended future audience | Inpatients and separately approved representatives | +| Engineering owner | Patient Experience Platform | +| Required independent owners | Clinical safety, privacy, security, identity, data governance, content, accessibility, language, support/operations, and release | +| Named approvals recorded | **None** | +| Next mandatory review | Before any route, network client, identity provider, source adapter, patient data, notification channel, messaging operation, non-production integration, or pilot is enabled | + +This model is intentionally strict. A threat or hazard marked **blocked by non-activation** +has not been made safe for live use; it is prevented only because the relevant capability +does not exist or cannot run. Removing that barrier without satisfying the named gates is a +release-blocking defect. + +## 1. Executive disposition + +### 1.1 Current decision + +The current Nightingale foundation is acceptable only as an offline, no-patient-data +application shell and governance substrate. It is **not acceptable for patient access, +clinical projection, care-team communication, notifications, representatives, or pilot +release**. + +Current executable barriers are unusually strong because: + +- the Android manifest requests no `INTERNET` permission; +- Android also explicitly denies cleartext traffic, trusts system certificate authorities + only, and contains no debug trust override; +- the iOS application contains no network client; +- the iOS Release bundle carries an exact offline-foundation privacy manifest with no + tracking or collected-data declaration and only app-local `UserDefaults` reason + `CA92.1`; +- the Nightingale OpenAPI foundation has zero paths and a non-routable `.invalid` server; +- Laravel registers no Nightingale route and binds no identity or inpatient-source port; +- every configuration and contract activation field is false; +- native product-boundary constants deny live patient and staff-endpoint access; +- local storage contains only two nonclinical presentation preferences; +- protected-state implementations are dormant and tested only with synthetic canaries; and +- no production database, patient, principal, encounter, message, or clinical projection + was used to prepare or verify this model. + +These barriers reduce current exposure; they do not close the inherent high and critical +risks of a future inpatient application. + +### 1.2 Safety conclusion + +The most consequential future hazards are: + +1. disclosure to the wrong patient or representative; +2. display of the wrong encounter or stale, unreleased, corrected, or retracted clinical + information; +3. false assurance that a patient message was delivered, monitored, or escalated; +4. privacy loss on a shared, lost, captured, backed-up, or compromised device; +5. inaccessible or misunderstood information causing delayed questions or care; +6. cross-product leakage from Hummingbird staff capabilities into Nightingale; and +7. activation without an auditable kill switch, incident process, rollback, and named + clinical/privacy/security approval. + +All seven remain activation-blocking. + +## 2. Method and authoritative inputs + +This document combines: + +- **NIST Cybersecurity Framework 2.0** as a high-level Govern, Identify, Protect, Detect, + Respond, and Recover outcome taxonomy: + [NIST CSF 2.0](https://www.nist.gov/publications/nist-cybersecurity-framework-csf-20); +- **OWASP Mobile Application Security Verification Standard** as a mobile control-domain + checklist for storage, cryptography, authentication, network, platform, code, + resilience, and privacy: + [OWASP MASVS](https://mas.owasp.org/MASVS/); +- **HHS Office for Civil Rights risk-analysis guidance** for confidentiality, integrity, + and availability of electronic protected health information: + [HHS Final Guidance on Risk Analysis](https://www.hhs.gov/hipaa/for-professionals/security/guidance/final-guidance-risk-analysis/index.html); +- **HHS mobile-device privacy guidance** as a reminder that health data on personal devices + creates privacy exposure beyond the application's direct control: + [HHS mobile privacy guidance](https://www.hhs.gov/hipaa/for-professionals/privacy/guidance/cell-phone-hipaa/index.html); +- **NHS DCB0129/DCB0160 lifecycle concepts** as a clinical-risk reference for + proportionate processes, maintained safety documentation, supplier/deployer + responsibilities, and named clinical safety ownership: + [NHS England 2026 supporting information](https://www.england.nhs.uk/long-read/national-review-of-clinical-risk-management-standardsdcb0129-and-dcb0160-supporting-information/); +- STRIDE categories for security threat enumeration; and +- repository-specific misuse cases, clinical failure modes, platform behavior, and + activation controls. + +These sources are methodological inputs. This model does **not** claim NIST, MASVS, HIPAA, +DCB0129, DCB0160, medical-device, privacy-law, or other regulatory compliance. Applicability +and conformity require independent legal, privacy, security, and clinical review. + +## 3. Assessment rules + +### 3.1 Severity + +| Level | Security/privacy consequence | Patient-safety consequence | +| --------------- | ------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------ | +| S1 — negligible | No sensitive data affected; quickly reversible inconvenience | No plausible injury; transient inconvenience | +| S2 — minor | Limited nonclinical exposure or short-lived loss of service | Temporary confusion or delay with an obvious workaround | +| S3 — moderate | Limited patient-data exposure, integrity loss, or material service interruption | Temporary harm, delayed care, or additional intervention may occur | +| S4 — major | Broad/sensitive disclosure, persistent account compromise, or serious integrity/availability loss | Serious temporary harm, preventable deterioration, or prolonged intervention | +| S5 — severe | Catastrophic confidentiality/integrity failure or systemic compromise | Death, permanent harm, severe deterioration, or failure to obtain urgent care is plausible | + +### 3.2 Likelihood if activated without the stated gates + +| Level | Meaning | +| ------------- | -------------------------------------------------------------------------- | +| L1 — rare | Requires exceptional conditions and multiple independent failures | +| L2 — unlikely | Credible but not expected in ordinary operation | +| L3 — possible | Can occur through a realistic defect, outage, misuse, or workflow mismatch | +| L4 — likely | Expected repeatedly without a dedicated control | +| L5 — frequent | Inherent or routine when the capability is used | + +### 3.3 Activation-risk score + +`Activation risk = severity × likelihood` + +| Score | Rating | Required disposition | +| ----- | -------- | ------------------------------------------------------------------------------------------------------------------------------- | +| 20–25 | Critical | Capability remains disabled; named executive, clinical, privacy, security, and release authorities must review closure evidence | +| 12–19 | High | Capability remains disabled until verified controls, owner, residual-risk decision, and rollback exist | +| 6–11 | Moderate | Control and test evidence required before release; any acceptance is time-bound and named | +| 1–5 | Low | Track and verify; low never means exempt from privacy or clinical review | + +The score estimates risk **if the relevant capability were activated without its gates**. +Current exposure is reported separately: + +- `blocked`: executable non-activation prevents the scenario today; +- `present-foundation`: the exposure exists in the current offline shell; +- `future-design`: the component is only a candidate and has no runtime path; or +- `external`: the exposure is partly controlled by the device, operating system, + institution, or patient environment. + +No numerical “residual risk” is accepted by this document. + +## 4. System and data-flow model + +### 4.1 Current executable flow + +```text +Patient or tester + | + v +Nightingale iOS / Android offline foundation + | | + | +--> OS accessibility and lifecycle signals + | + +--> two nonclinical, device-local presentation preferences + | + +--> dormant protected-state primitive + (synthetic test canary only; no production caller) + +NO EDGE --> network, Zephyrus API, identity provider, production database, + inpatient source, care team, message router, push provider, or analytics + +Laravel repository foundation + | + +--> unbound identity and inpatient-source interfaces + +--> fail-closed precondition types + +--> zero registered Nightingale routes and zero OpenAPI operations +``` + +### 4.2 Future conceptual flow under review + +The following is a threat-model boundary, not an implementation design: + +```text +Personal/shared device + -> platform app and local protected state + -> network edge/API gateway + -> independent Nightingale identity/session service + -> principal-to-patient and representative authority + -> encounter-access and current-inpatient gates + -> released patient projection service + -> authoritative clinical/operational sources + -> governed care-team routing and messaging + -> notification provider with minimized payloads + -> append-only audit, detection, support, incident, and rollback services +``` + +Every arrow introduces a trust boundary that is currently absent and must receive its own +contract, authorization tests, abuse cases, telemetry policy, failure behavior, and +rollback. + +### 4.3 Repository evidence + +- [Empty/default-off OpenAPI foundation](./api-contract/nightingale-foundation.v0.json) +- [Route, compatibility, identity, and source ADR](./ROUTE-COMPATIBILITY-IDENTITY-SOURCE-ADR-2026-07-26.md) +- [Contract ownership and authorization matrix](./CONTRACT-OWNERSHIP-AND-AUTHORIZATION-MATRIX-2026-07-26.md) +- [Identity/session/recovery/source held candidate](./IDENTITY-SESSION-RECOVERY-AND-SOURCE-CANDIDATE-DECISION-2026-07-26.md) +- [Encounter-access held candidate](./ENCOUNTER-ACCESS-CANDIDATE-DECISION-2026-07-26.md) +- [Today projection held candidate](./TODAY-PROJECTION-CANDIDATE-DECISION-2026-07-26.md) +- [Presentation-preferences foundation decision](./PRESENTATION-PREFERENCES-FOUNDATION-DECISION-2026-07-26.md) +- [Accessibility/layout matrix](./FOUNDATION-ACCESSIBILITY-LAYOUT-MATRIX-2026-07-26.md) +- [Laravel default-deny configuration](../../config/nightingale.php) +- [iOS application root](../../nightingale/iosApp/Nightingale/NightingaleApp.swift) +- [Android application activity](../../nightingale/androidApp/app/src/main/java/net/acumenus/nightingale/MainActivity.kt) + +## 5. Protected assets + +| Asset ID | Asset | Required property | Current state | +| -------- | ---------------------------------------- | ------------------------------------------------------------------------------- | ------------------------------------------------------- | +| AST-01 | Independent Nightingale product identity | No Hummingbird/staff realm, endpoint, storage, bundle, or role confusion | Implemented and mechanically checked | +| AST-02 | Patient principal and proofing result | Authentic, self-only by default, purpose-bound, revocable | Not implemented | +| AST-03 | Representative authority | Legally/operationally valid, scoped, visible, revocable, non-inferential | Not implemented; prohibited | +| AST-04 | Session binding and token family | Confidential, short-lived, rotated, replay-resistant, revocable | Dormant storage descriptor only | +| AST-05 | Principal-to-patient link | Correct, current, conflict-detected, auditable | Not implemented | +| AST-06 | Current inpatient context | Authoritative, encounter-specific, fresh, contradiction-aware | Held candidate; no adapter/query | +| AST-07 | Encounter-access grant | Self-only initial cardinality, scope/purpose/effective-window enforced | Held candidate; no operation | +| AST-08 | Patient-visible clinical projection | Released, field-governed, fresh, understandable, correctable/retractable | Held Today candidate; no operation | +| AST-09 | Care-team directory and routing | Current, accountable, service-window and escalation aware | Not implemented | +| AST-10 | Patient messages and attachments | Confidential, ordered, idempotent, deliverable, amendable, auditable | Not implemented | +| AST-11 | Notification metadata/payload | Minimized, non-revealing, revocable, device-aware | Not implemented | +| AST-12 | Audit and security telemetry | Complete, tamper-evident, access-controlled, privacy-minimized | Not implemented | +| AST-13 | Local presentation preferences | Nonclinical, Nightingale-only, non-roaming | Implemented | +| AST-14 | Future volatile composition | Process-lifetime only, cleared at sensitive transitions | Primitive implemented; no live input UI | +| AST-15 | Protected local state | Device-bound, corrupt-state fail-closed, deletable | Dormant primitives implemented | +| AST-16 | Signing/build/release/media artifacts | Provenance, exact SHA, no Debug hooks, licensed assets, authorized distribution | Local code/media lineage only; no distribution approval | +| AST-17 | Support and recovery evidence | Authentic, least-privilege, non-disclosing, auditable | Not implemented | +| AST-18 | Clinical safety and content approvals | Named, versioned, scoped, expiring, reviewable | None recorded | + +## 6. Actors and attacker assumptions + +### 6.1 Legitimate actors + +- inpatient using a personal, shared, borrowed, accessibility-managed, or institution-owned + device; +- separately approved representative; +- bedside nurse, physician, pharmacist, care manager, interpreter, and other care-team + roles; +- support, privacy, security, clinical-safety, content, data-governance, and release staff; +- identity, EHR, ADT, FHIR, messaging, push, audit, and app-store/platform providers; and +- automated build, test, signing, and deployment systems. + +### 6.2 Threat actors and failure sources + +- another person with physical or logical access to the patient's device; +- an unauthorized or formerly authorized representative; +- a remote attacker using credential stuffing, phishing, replay, session theft, API + enumeration, or denial of service; +- malware, overlay, instrumentation, rooted/jailbroken devices, or compromised operating + systems; +- malicious or vulnerable third-party dependencies, SDKs, build runners, signing systems, + or distribution accounts; +- an insider exceeding legitimate purpose or scope; +- accidental staff misrouting, wrong-patient linkage, source-data error, release error, or + support disclosure; +- network, identity, source, notification, messaging, or time-synchronization failure; and +- product defects, ambiguous language, accessibility barriers, retry races, partial + responses, or stale offline state. + +### 6.3 Assumptions not permitted + +Nightingale must not assume that: + +- possession of a device proves patient identity; +- a platform biometric proves the correct patient relationship; +- one source row proves the current encounter; +- a successful HTTP response proves content release, freshness, or clinical correctness; +- a submitted message is delivered, read, monitored, or appropriate for urgent care; +- a representative remains authorized; +- the device is private, uncompromised, online, correctly timed, or backup-free; +- an absent section means that nothing is planned; +- patients understand internal clinical/operational vocabulary; +- staff and patient product identifiers are interchangeable; or +- a green automated test constitutes clinical, privacy, accessibility, or release approval. + +## 7. Trust boundaries + +| Boundary ID | Boundary | Primary threats | Current status | +| ----------- | --------------------------------------------------- | -------------------------------------------------------------------- | --------------------------------------- | +| TB-01 | Patient/other person to unlocked device | shoulder surfing, shared-device disclosure, coercion, mistaken user | Present-foundation | +| TB-02 | App UI to local preference storage | tampering, backup/sync, clinical-state confusion | Present-foundation; nonclinical only | +| TB-03 | App to Keychain/Keystore/private ciphertext | theft, corruption, replay, deletion failure, key loss | Dormant synthetic use only | +| TB-04 | App lifecycle to task switcher/screenshot/recording | captured PHI, stale visible UI | Present-foundation; platform-asymmetric | +| TB-05 | App package to OS/runtime | tampering, hooks, overlays, debug artifacts, vulnerable dependencies | Present-foundation | +| TB-06 | Device to network edge | interception, downgrade, traffic analysis, outage | Absent; future-design | +| TB-07 | Edge to identity/session service | spoofing, replay, token substitution, confused realm | Absent; future-design | +| TB-08 | Identity to patient/representative linkage | wrong patient, stale authority, privilege escalation | Absent; future-design | +| TB-09 | Access service to current-inpatient source | wrong encounter, stale/contradictory status, source outage | Absent; future-design | +| TB-10 | Source to released projection | unreleased/internal/stale/corrected content | Absent; future-design | +| TB-11 | Patient message to care-team router | wrong team, duplication, loss, urgent-message misuse | Absent; future-design | +| TB-12 | Backend to push provider/lock screen | PHI disclosure, token reuse, delayed/duplicate notification | Absent; future-design | +| TB-13 | Runtime to audit/telemetry/support | missing events, sensitive logging, insider access, repudiation | Absent; future-design | +| TB-14 | Source/build to signed store artifact | dependency compromise, secret injection, wrong signing identity | Local CI only; distribution absent | + +## 8. Implemented foundation-control catalog + +| Control ID | Implemented control | Evidence | Claim boundary | +| ---------- | -------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------- | ----------------------------------------------------------------------------------------- | +| CTRL-001 | Independent `net.acumenus.nightingale` native identity and namespaces | Native projects, protected/preference namespaces | Does not create an independent identity realm or provider | +| CTRL-002 | Zero OpenAPI paths, empty components/security, `.invalid` server | `nightingale-foundation.v0.json` and verifier | Does not prove a future operation is safe | +| CTRL-003 | All backend/network/identity/source/disclosure/mutation/production flags false | `config/nightingale.php` and backend verifier | Flags are not a future runtime kill switch | +| CTRL-004 | Unconfigured identity and inpatient-source implementations return unavailable | `app/Nightingale/**` and unit tests | No container binding or HTTP caller exists | +| CTRL-005 | Precondition gate only permits later evaluation; it never grants access | `NightingaleEncounterAccessPreconditionGate.php` | Later grant, purpose, resource, release, and audit gates are absent | +| CTRL-006 | Native compile-time live-access and staff-endpoint constants are false | iOS/Android product boundaries | Constants are defense-in-depth, not server authorization | +| CTRL-007 | Android requests no `INTERNET` permission | Android manifest, Gradle verifier, APK inspection | iOS has no equivalent manifest permission model | +| CTRL-008 | Android `FLAG_SECURE` blocks ordinary screenshots/recordings | `NightingalePrivacyPolicy.kt`, instrumentation evidence | Does not defeat cameras, compromised OS, overlays, or every capture path | +| CTRL-009 | iOS covers content whenever the scene is inactive | `NightingalePrivacyProtectedRoot` and XCUITest | Does not block active-screen screenshots or physical observation | +| CTRL-010 | Android covers content on pause and clears volatile input | `MainActivity.onPause` | Lifecycle callbacks are not a compromised-runtime defense | +| CTRL-011 | iOS Keychain uses data-protection Keychain, non-synchronizable, `WhenUnlockedThisDeviceOnly` | `NightingaleProtectedState.swift` | Dormant; no real session format, biometric binding, or threat approval | +| CTRL-012 | Android Keystore uses AES-256-GCM, randomized 12-byte IV, 128-bit tag, AAD, private ciphertext | `NightingaleProtectedState.kt` | Key does not currently require user authentication | +| CTRL-013 | Android write failure removes prior key/ciphertext; deletion verifies both | Protected-state tests | Does not prove behavior on all OEM, backup, restore, or device-compromise states | +| CTRL-014 | Empty protected values are rejected and corrupt/unavailable storage fails closed | Native stores and tests | No recovery/support UX exists | +| CTRL-015 | Android backup and device transfer are disabled/excluded | Manifest plus backup/data-extraction rules | External device/OS/vendor behavior still requires release verification | +| CTRL-016 | Presentation preferences are nonclinical, product-local, and non-roaming | Native preference code and decision record | They are not consent, account, clinical, or accessibility-profile records | +| CTRL-017 | Volatile drafts clear on inactive/logout/identity/recovery/revocation/removal boundaries | Native state primitives and tests | Immutable `String` memory is not reliably zeroized | +| CTRL-018 | Debug test hooks are compile-time or build-boundary constrained and absent from Release artifacts | Native source, binary/APK scans | Future diagnostic/logging code requires separate review | +| CTRL-019 | Accessibility policy honors stronger system/patient reduced-motion and imagery conditions | Presentation policy tests | Does not establish full WCAG or assistive-technology conformance | +| CTRL-020 | Minimum targets, semantic order, reflow, contrast, and true landscape are bounded and tested | Accessibility/layout matrix | Covers only the current offline shell | +| CTRL-021 | Hummingbird/legacy identifiers and staff endpoints are mechanically prohibited in Nightingale source | Product-boundary verifier | Future shared libraries/adapters need explicit architectural review | +| CTRL-022 | Candidate fixtures fail closed on unknown, contradictory, stale, or unreleased states | Candidate documents and self-testing verifiers | Candidates are non-runnable and not clinically approved | +| CTRL-023 | Exact-SHA CI and scoped publication evidence are retained | Draft PR and devlog | No distribution signing, store, deployment, or pilot evidence exists | +| CTRL-024 | No production database work is authorized by this product stream | Plan, contract, and activation holds | A future source integration needs separate, path-scoped authorization | +| CTRL-025 | Exact seven-photo catalog is metadata-stripped, nonessential, cross-platform deterministic, and hidden under stronger contrast/transparency policy | Asset manifest/verifier, native UI/tests, Release artifact scans | Durable rights/attribution and human patient review remain open | +| CTRL-026 | Exact iOS offline privacy manifest plus Android cleartext denial, system-only trust, no network permission, and backup exclusion | Privacy-control record, native tests, Release artifact scans | Store declarations, future data flows, signed artifacts, and privacy approval remain open | +| CTRL-027 | Exact 15-string English source contract, three shell headings, restrained status announcements, and Debug-only text-expansion/RTL exercises | Language-readiness record, native tests, Release artifact scans | No translation, human AT review, future-screen coverage, or WCAG conformance is approved | + +## 9. Security and privacy threat register + +| Threat ID | STRIDE/domain | Scenario | Activation risk | Current exposure/control | Required closure before exposure | +| ----------- | ---------------------- | ------------------------------------------------------------------------------------------------------------ | ------------------- | ------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------ | +| THR-S-001 | Spoofing | Attacker authenticates as an inpatient through weak enrollment, credential theft, replay, or realm confusion | S5×L4 = 20 Critical | Blocked: no identity/provider/session/network | GATE-03 through GATE-07; adversarial identity tests and penetration test | +| THR-S-002 | Spoofing | Representative claims, retains, or expands authority without valid patient/legal basis | S5×L3 = 15 High | Blocked; representatives prohibited | GATE-05, relationship lifecycle, patient visibility, revocation, legal/privacy approval | +| THR-S-003 | Spoofing | Device possession or local biometric is treated as sufficient server identity | S5×L3 = 15 High | Blocked; no local auth path | Server-side proof, session binding, step-up policy, lost/shared-device cases | +| THR-T-001 | Tampering | Protected local state is modified, replayed, rolled back, or paired with the wrong key/ciphertext | S4×L3 = 12 High | Dormant Keychain/Keystore controls; corruption fails closed | Session format, anti-replay binding, restore/upgrade tests, mobile penetration test | +| THR-T-002 | Tampering | API response, cache, or source payload is modified or partially substituted | S5×L3 = 15 High | Blocked: no client/API/cache | TLS, server authorization, schema integrity, complete-response validation, no partial trust | +| THR-T-003 | Tampering | Repackaged, hooked, debugged, or overlaid app changes patient-visible content or captures input | S5×L3 = 15 High | Platform defaults; Android secure flag; no attestation/resilience policy | GATE-16, signed-distribution evidence, tamper posture, overlay/input review | +| THR-R-001 | Repudiation | Patient, representative, staff, source, or system action cannot be reconstructed | S4×L3 = 12 High | Blocked; no live actions and no audit service | Append-only event model, actor/purpose/correlation, retention/access policy, clock strategy | +| THR-I-001 | Information disclosure | PHI appears in screenshot, recording, task switcher, shoulder view, camera, or shared device | S4×L4 = 16 High | Present-foundation UI has no PHI; Android secure flag; lifecycle covers | GATE-13/14/16, iOS capture decision, privacy UX, physical/shared-device testing | +| THR-I-002 | Information disclosure | PHI enters logs, crash reports, analytics, metrics, support tools, or diagnostics | S4×L4 = 16 High | Blocked: no PHI/telemetry design | Data-flow inventory, field allowlist, redaction tests, vendor contracts, access/retention policy | +| THR-I-003 | Information disclosure | Source identifiers, encounter IDs, staff internals, inferred risk, or unreleased notes reach the client | S5×L3 = 15 High | Candidate contracts prohibit source/cross-product identifiers | Projection allowlist, serialization negative tests, code review, clinical/privacy approval | +| THR-I-004 | Information disclosure | Push content or device token links a patient to diagnosis, location, or care | S4×L4 = 16 High | Blocked: no notifications/provider | GATE-13, minimized generic payload, token lifecycle, lock-screen review, delivery tests | +| THR-I-005 | Privacy | Backup, cloud sync, device transfer, clipboard, keyboard, or OS service retains sensitive state | S4×L3 = 12 High | Android backup excluded; iOS protected state device-only; drafts not live | Platform release tests, input-class policy, clipboard/keyboard prohibition, deletion proof | +| THR-I-006 | Privacy | Third-party SDK or advertising/tracking identifier observes health-app behavior | S4×L3 = 12 High | No application analytics/ad SDK identified; platform libraries only | Dependency/SDK allowlist, privacy manifest/declarations, network observation, vendor review | +| THR-D-001 | Denial of service | Credential stuffing, enumeration, abusive polling, or message flooding exhausts service | S4×L4 = 16 High | Blocked: no service | Rate limits by purpose/principal/device/network, abuse detection, safe errors, capacity tests | +| THR-D-002 | Availability | Identity, source, network, push, messaging, or audit outage produces blank or misleading UI | S5×L3 = 15 High | Current UI explicitly says access unavailable | State-specific downtime/offline UX, cached-content policy, monitoring, support and rollback | +| THR-E-001 | Elevation/IDOR | Principal changes opaque handles or calls another patient's encounter/thread/resource | S5×L4 = 20 Critical | Blocked: zero operations | GATE-06/07/08, object-level authorization, cross-patient negatives, no existence oracle | +| THR-E-002 | Elevation | Client-side flags or local state are treated as authorization | S5×L3 = 15 High | No server operation; docs prohibit local authorization | Server-side lattice on every read/write, fail-closed defaults, mutation/IDOR tests | +| THR-E-003 | Elevation | Debug hook, synthetic API, hidden route, or test credential escapes into Release | S5×L2 = 10 Moderate | Release scans and zero-route verifiers | Continue artifact scans; distribution artifact/SBOM inspection and signing provenance | +| THR-CFG-001 | Misconfiguration | Feature/configuration change activates route, query, disclosure, or client independently | S5×L3 = 15 High | Code-owned false fields; no route/client/provider | Multi-party activation manifest, interlocked gates, environment-diff proof, kill switch | +| THR-SC-001 | Supply chain | Dependency, build runner, package registry, signing key, or store account is compromised | S5×L3 = 15 High | Exact-SHA CI; generated source-hash-bound Release runtime inventory | GATE-16, artifact verification/provenance, vulnerability policy, signing/access audit | +| THR-OPS-001 | Social engineering | Support/recovery staff disclose patient status or reset access for an attacker | S5×L3 = 15 High | Blocked: no recovery/support flow | GATE-04/15, scripts, proofing, dual control, non-disclosure, audit, red-team cases | +| THR-INS-001 | Insider | Authorized staff accesses, routes, exports, or alters patient data without purpose | S5×L3 = 15 High | Blocked: no Nightingale patient operations | Least privilege, purpose-of-use, access review, anomaly detection, sanctions and audit | + +## 10. Clinical hazard log + +| Hazard ID | Hazardous situation and initiating causes | Plausible harm | Activation risk | Current barrier | Required control/evidence | +| --------- | ----------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------- | ------------------- | ------------------------------------------ | --------------------------------------------------------------------------------------------------------------- | +| HZ-001 | Wrong principal is linked to a real patient through proofing, linkage, merge, or support error | Severe privacy breach; incorrect action based on another patient's care | S5×L3 = 15 High | No identity/linkage | Independent proofing/linkage design, conflict handling, cross-patient tests, named identity/privacy approval | +| HZ-002 | Correct principal receives the wrong encounter because first-row, stale ADT, facility, class, or overlap logic is wrong | Misunderstanding care plan/location/discharge; delayed questions or care | S5×L3 = 15 High | No source adapter/query | Authoritative current-inpatient rule, contradiction state, cardinality policy, freshness, synthetic integration | +| HZ-003 | Released information is stale but presented as current | Patient follows outdated schedule, location, medication, or discharge expectation | S5×L3 = 15 High | No projection | Per-field source time, approved thresholds, explicit stale/withhold state, outage tests | +| HZ-004 | Corrected or retracted information remains visible or cached | Patient acts on withdrawn or erroneous information | S5×L3 = 15 High | No projection/cache | Append-only correction lineage, retraction withhold, cache purge, notification and audit tests | +| HZ-005 | Draft, review, internal note, inferred risk, source identifier, or staff-only content reaches the patient | Distress, privacy harm, misinterpretation, altered care relationship | S5×L3 = 15 High | Zero operations; candidate allowlists | Released-projection service, deny-by-default serializer, field approvals, negative fixtures | +| HZ-006 | Translation, terminology, reading level, or uncertainty language changes clinical meaning | Misunderstanding, delayed escalation, inappropriate expectations | S5×L3 = 15 High | No clinical content | Clinical + language approval per field, interpreter path, versioned copy, comprehension/usability evidence | +| HZ-007 | Empty/unavailable/withheld section appears to mean “nothing is planned” | Patient fails to ask about missing care, tests, or discharge work | S4×L4 = 16 High | Current shell says live access unavailable | Explicit state vocabulary, reason-safe explanation, contact path, comprehension tests | +| HZ-008 | Messaging UI implies urgent messages are monitored or substitutes for emergency help | Delay in urgent assessment or emergency treatment | S5×L4 = 20 Critical | No messaging operation | Urgent-use policy, prominent reviewed guidance, blocked urgent categories, service windows, escalation proof | +| HZ-009 | Submitted/accepted message is described as delivered, seen, or actioned | Patient waits for a response that will not occur | S5×L3 = 15 High | No messaging | State vocabulary tied to end-to-end evidence, delivery/read semantics, timeout and fallback | +| HZ-010 | Ambiguous network retry duplicates a message, amendment, closure, or request | Conflicting work, repeated interventions, confusion, privacy exposure | S4×L3 = 12 High | No mutation | Idempotency keys, stable command outcome, retry/reconciliation tests, append-only audit | +| HZ-011 | Message routes to the wrong team, inactive pool, or unaffiliated staff member | Delay, non-response, disclosure, wrong advice | S5×L3 = 15 High | No routing/team directory | Accountable routing owner, membership/facility/encounter checks, service window and failover | +| HZ-012 | Discharged, transferred, deceased, merged, or closed context remains actionable | Disclosure or advice disconnected from current care | S5×L3 = 15 High | Source state cannot be current | Real-time revocation/closure semantics, race tests, read/write reauthorization | +| HZ-013 | Representative sees content outside approved relationship/scope or after revocation | Privacy harm, coercion, unsafe family dynamics, loss of trust | S5×L3 = 15 High | Representatives prohibited | Legal/privacy model, granular scope, sensitive-content exceptions, patient visibility/revoke | +| HZ-014 | Screen-reader order, focus, target size, contrast, language expansion, or motor access fails | Patient cannot understand or communicate, causing delay or exclusion | S4×L4 = 16 High | Bounded offline-shell tests only | Full WCAG/platform matrix, human AT testing, language/RTL, physical-device and patient review | +| HZ-015 | PHI is visible on lock screen, task switcher, screenshot, shared/lost device, or nearby observer | Privacy breach, stigma, coercion, identity theft | S4×L4 = 16 High | No PHI; partial platform controls | Minimized UI/push, reauthentication, capture posture, privacy mode, lost-device response | +| HZ-016 | Device compromise or repackaged app alters patient-visible content | Patient trusts malicious or false clinical/communication information | S5×L2 = 10 Moderate | No live data; store distribution absent | Signing/provenance, update/integrity posture, server trust, compromise response, user guidance | +| HZ-017 | Offline/outage state shows cached data without age/source or silently drops actions | Patient believes stale information or communication is current | S5×L3 = 15 High | No cache/network/action | Approved offline policy per field/action, timestamps, no offline mutation unless reconciled | +| HZ-018 | Partial/malformed response is rendered instead of withheld | Mixed-patient or internally inconsistent page appears authoritative | S5×L3 = 15 High | No client decoder | Atomic validation, unknown-enum withholding, schema negatives, no partially trusted object | +| HZ-019 | Clinical/operational source is unavailable or contradictory but adapter guesses current state | Wrong encounter/content disclosed | S5×L3 = 15 High | Unavailable/inconsistent fail closed | Reconciliation rules, authoritative ownership, contradiction alerting, no guessed thresholds | +| HZ-020 | Patient or staff over-relies on Nightingale as the complete legal/clinical record | Missing context is treated as definitive | S4×L3 = 12 High | Foundation denies live access | Product wording, scope disclosure, source/correction path, training, patient comprehension | +| HZ-021 | Recovery/identity transition leaves old protected state or draft bound to a new principal | Cross-account disclosure or action | S5×L3 = 15 High | No live identity; deletion primitives | Transactional identity switch, verified deletion, new realm/session, failure-withhold tests | +| HZ-022 | Kill switch, rollback, or correction cannot propagate quickly during an incident | Continued exposure or harmful guidance at scale | S5×L3 = 15 High | No live capability | GATE-15/17, tested remote disable/withhold, rollback SLO, incident tabletop | + +No hazard above is accepted. The current disposition for every high/critical live-data +hazard is **keep the capability disabled**. + +## 11. Abuse and misuse cases + +| Abuse ID | Attempt | Required safe result | +| --------- | ---------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------ | +| ABUSE-001 | Change an encounter/thread/message handle to another value | Withhold without confirming resource existence; audit the denied attempt | +| ABUSE-002 | Replay an expired, rotated, revoked, or already-used session binding | Deny, delete unusable local state where appropriate, require safe recovery | +| ABUSE-003 | Present a Hummingbird staff token or legacy patient token to Nightingale | Reject realm/product mismatch; never alias, proxy, redirect, or silently migrate | +| ABUSE-004 | Enroll using a room number, date of birth, shared email, or easily known fact | Fail proofing; rate-limit and avoid revealing whether a patient exists | +| ABUSE-005 | Support caller claims to be a patient/representative and asks whether someone is admitted | Disclose neither identity nor admission; follow approved proofing/escalation | +| ABUSE-006 | Revoked representative continues using cached credentials | Deny every read/write after revocation and purge/withhold cached content | +| ABUSE-007 | Send urgent symptoms through a general message topic | Prevent false monitoring assurance; show approved urgent/emergency direction | +| ABUSE-008 | Toggle airplane mode immediately after sending | Reconcile deterministic command state; never label ambiguous submission delivered | +| ABUSE-009 | Rapidly retry send/close/amend commands | Apply idempotency/concurrency rules; no duplicate or destructive lost update | +| ABUSE-010 | Inject unknown enum, missing release field, mixed encounter handle, or malformed item | Reject/withhold the complete affected projection; log safe diagnostic metadata | +| ABUSE-011 | Capture, mirror, overlay, or background the app | Apply approved platform privacy controls and reauthentication; never rely on one flag | +| ABUSE-012 | Restore app data to a different device or account | Device-bound protected state is unusable; app fails closed and supports safe recovery | +| ABUSE-013 | Root/jailbreak/hook the app and alter a care message | Apply approved compromised-device posture; server remains authoritative and auditable | +| ABUSE-014 | Dependency or build step inserts a network endpoint/debug credential | CI/artifact/provenance checks fail; signing/release is blocked | +| ABUSE-015 | Source returns two plausible current encounters | Return inconsistent/withhold; do not choose first or latest without approved rules | +| ABUSE-016 | Clinical item is corrected while patient is offline | Old item becomes visibly stale/withheld on reconnection; correction lineage is auditable | +| ABUSE-017 | Hide imagery, substitute a catalog file, inject metadata, or imply that a bird/photo represents care state | Exact asset/set/hash checks fail on substitution or metadata; essential meaning/action remain textual and semantic | +| ABUSE-018 | Switch identity or recover account while a draft is in memory | Clear draft and protected binding before new identity can display or submit it | + +## 12. Platform deltas and unresolved asymmetries + +| Area | iOS foundation | Android foundation | Required convergence | +| ------------------ | ------------------------------------------------------------------------------------------ | --------------------------------------------------------------- | ------------------------------------------------------------------------------------------------- | +| Network absence | No network client in source/binary scans; exact offline privacy manifest | No `INTERNET`; explicit cleartext denial and system-only trust | Add approved network stacks together with TLS/endpoint tests; re-review privacy declarations | +| Background privacy | SwiftUI privacy cover when scene inactive | Cover on pause plus `FLAG_SECURE` | Decide active-screen capture policy, physical observation, external display, and recents behavior | +| Protected state | Data-protection Keychain, non-sync, when-unlocked-this-device-only | Keystore AES-GCM + private ciphertext, no user-auth requirement | Approve session threat model, authentication binding, restore/upgrade/delete behavior | +| Backup/transfer | Protected value is this-device-only; application-wide backup behavior not release-verified | Backup false plus complete cloud/transfer exclusions | Distribution-artifact backup/restore tests on both platforms | +| Volatile strings | Reference cleared; immutable `String` not guaranteed zeroized | Reference cleared; immutable `String` not guaranteed zeroized | Input-type/keyboard/clipboard policy and memory-sensitive design | +| Capture testing | Lifecycle cover XCUITest | Secure-flag black capture and lifecycle instrumentation | Approved physical-device test matrix without weakening production controls | +| Release hardening | Release build and hook scan; no distribution artifact | Release minification currently disabled | Security owner decides symbol/obfuscation/tamper posture and verifies signed store artifacts | +| Compromised device | No jailbreak/attestation policy | No root/attestation/overlay policy beyond platform defaults | Risk-based policy, support path, accessibility impact, false-positive handling | +| Accessibility | True-landscape accessibility5 shell journey | Font-scale-2.0 landscape shell journey | Human VoiceOver/TalkBack, keyboard/switch access, language/RTL, all future screens | + +## 13. Mandatory activation gates + +| Gate ID | Gate | Minimum evidence | +| ------- | --------------------------------------- | ------------------------------------------------------------------------------------------------------------- | +| GATE-01 | Operation and owner | Versioned Nightingale-owned operation, accountable owner, compatibility/deprecation decision, no legacy alias | +| GATE-02 | Data minimization | Field inventory, purpose, provenance, release class, source-identifier prohibition, privacy approval | +| GATE-03 | Identity realm and provider | Independent realm/provider, proofing assurance, provider threat review, non-disclosure errors | +| GATE-04 | Enrollment, session, and recovery | Invitation authority, rates, expiry/rotation/revocation, support scripts, lost/shared-device flow | +| GATE-05 | Relationship and representative policy | Self-only initial decision or approved legal/privacy representative model with lifecycle tests | +| GATE-06 | Patient linkage | Conflict/merge/deceased/revoked handling, authoritative owner, cross-patient negative evidence | +| GATE-07 | Encounter/current-inpatient source | Approved adapter, cohort/status mapping, cardinality, contradiction, freshness, downtime, no guessed state | +| GATE-08 | Authorization and IDOR | Scope/purpose/effective-window/resource ownership on every read/write; adversarial object tests | +| GATE-09 | Clinical release/content | Named field-level clinical/content/language approvals, understandable vocabulary, uncertainty rules | +| GATE-10 | Freshness/correction/retraction/offline | Approved clocks/thresholds, visible state, cache/purge rules, complete synthetic cases | +| GATE-11 | Messaging/routing | Accountable team, topic/pool/member/service-window/escalation policy, end-to-end delivery states | +| GATE-12 | Mutation integrity | Idempotency, concurrency, append-only history, retry/reconciliation and race tests | +| GATE-13 | Notification privacy | Generic/minimized payload, token lifecycle, lock-screen/device review, delayed/duplicate/revoked tests | +| GATE-14 | Accessibility and patient comprehension | WCAG/platform matrix, patient advisors, VoiceOver/TalkBack, motor, language/RTL, physical devices | +| GATE-15 | Audit, support, detection, incident | Event model, privacy-minimized telemetry, monitoring, support, kill switch, breach/safety escalation | +| GATE-16 | Mobile/application security | MASVS-scoped assessment, dependency review, signed-artifact analysis, penetration test, residual risks | +| GATE-17 | Rollback and recovery | Tested route/client/source/content disable, correction/retraction propagation, rollback SLO and owner | +| GATE-18 | Non-production integration | Synthetic/deidentified environment, no production credentials/data, bounded manifest, teardown proof | +| GATE-19 | Pilot authorization | Named scope/expiry/users/devices/facilities/features, training/support, go/no-go and stop criteria | +| GATE-20 | Production release | Protected-main exact SHA, CI, signed artifacts, store records, deployment checks, no implicit migration | + +Passing one gate never implies another. In particular: authenticated is not authorized; +authorized is not current inpatient; current inpatient is not content release; content +release is not messaging permission; deployment is not pilot approval. The bounded +route-free activation foundation now gives five of these separations distinct types: +institutional clinical approval, patient-content release, feature activation, pilot +enrollment, and source-connector deployment. Its exhaustive 32-row proof holds 31 +incomplete combinations; the all-positive row still proceeds only to operation-specific +release evaluation and does not satisfy the other gates in this table. + +## 14. Verification program + +| Verification ID | Required verification | Threats/hazards covered | Status | +| --------------- | --------------------------------------------------------------------------------------------------------------------- | ------------------------------------- | ------------------------------ | +| VER-001 | Contract zero-path/default-off, five-gate activation separation, 32-row truth table, and negative mutation self-tests | THR-CFG-001, THR-E-003 | Implemented foundation only | +| VER-002 | Backend unavailable/inconsistent truth tables and unbound-route proof | HZ-002, HZ-019 | Implemented foundation only | +| VER-003 | Native product/namespace/no-network/release-hook scans | THR-I-004, THR-E-003 | Implemented foundation only | +| VER-004 | Protected-state read/write/corrupt/delete synthetic canaries | THR-T-001, HZ-021 | Implemented foundation only | +| VER-005 | Accessibility contrast/order/reflow/target/landscape/text-expansion/RTL automation | HZ-014 | Implemented current shell only | +| VER-006 | Identity proofing, enrollment, replay, enumeration, session, step-up, recovery abuse suite | THR-S-001/003, THR-D-001, THR-OPS-001 | Not implemented | +| VER-007 | Representative grant/revoke/sensitive-content/cross-patient suite | THR-S-002, HZ-013 | Not implemented | +| VER-008 | IDOR/resource ownership/scope/purpose/effective-window matrix | THR-E-001/002 | Not implemented | +| VER-009 | Source reconciliation, overlap, merge, transfer, close, freshness, outage, race suite | HZ-002/003/012/019 | Candidate fixtures only | +| VER-010 | Field release/language/freshness/correction/retraction/offline atomic-decoder suite | HZ-003–007/017/018 | Today candidate fixtures only | +| VER-011 | Messaging route/delivery/idempotency/concurrency/urgent/downtime end-to-end suite | HZ-008–012 | Not implemented | +| VER-012 | Push payload/token/lock-screen/delay/duplicate/revoke suite | THR-I-004, HZ-015 | Not implemented | +| VER-013 | Logging/analytics/crash/support PHI canary and access/retention audit | THR-I-002/003/006 | Not implemented | +| VER-014 | Signed iOS/Android artifact MASVS assessment and penetration test | THR-T-003, THR-SC-001 | Not implemented | +| VER-015 | Human patient comprehension, language, interpreter, VoiceOver/TalkBack, motor review | HZ-006/007/014/020 | Not implemented | +| VER-016 | Tabletop for wrong patient, stale content, message outage, push leak, lost device, source compromise | HZ-001–022 | Not implemented | +| VER-017 | Kill-switch, correction/retraction, rollback, cache purge, recovery-time exercise | HZ-004/017/022 | Not implemented | +| VER-018 | Deidentified bounded non-production pilot rehearsal and teardown | Cross-cutting | Prohibited until approval | + +## 15. Detection, incident response, and recovery requirements + +### 15.1 Detection signals + +Before activation, the system needs privacy-minimized detection for: + +- repeated proofing/enrollment/recovery failures and patient-existence probing; +- session replay, reuse, revocation, unusual device/risk events, and realm mismatch; +- cross-patient handle access, denied scopes, relationship changes, and IDOR patterns; +- source contradiction, unexpected cardinality, stale observation, and projection + release/correction/retraction errors; +- message routing failures, delivery timeouts, duplicates, service-window violations, and + urgent-content policy events; +- push-token churn, revoked-device sends, payload policy violations, and provider outage; +- decoder/schema failures, unknown enums, partial objects, and client-version incompatibility; +- abnormal exports, support lookups, audit access, and privileged configuration changes; and +- artifact/signing/dependency provenance drift. + +Detection events must not themselves disclose clinical content or become an ungoverned +secondary patient record. + +### 15.2 Incident classes + +| Incident ID | Example | Immediate containment | +| ----------- | ------------------------------------------ | ---------------------------------------------------------------------------------------------------------- | +| INC-01 | Wrong-patient or representative disclosure | Disable affected access/projection, revoke sessions, preserve audit, privacy/clinical escalation | +| INC-02 | Stale/wrong/retracted clinical content | Withhold affected field/section globally, publish correction, purge cache, notify through approved channel | +| INC-03 | Message loss/misrouting/false delivery | Disable mutation or affected topic, expose service outage, reconcile queues, activate safe fallback | +| INC-04 | Push payload/token disclosure | Stop provider sends, revoke tokens, rotate credentials, assess lock-screen/device exposure | +| INC-05 | Identity/recovery compromise | Suspend enrollment/recovery, revoke families, require safe re-proofing, protect non-disclosure | +| INC-06 | Source or API compromise | Disable adapter/route, fail closed, isolate credentials, validate integrity before restoration | +| INC-07 | Mobile/supply-chain compromise | Stop release/distribution, revoke signing/store access as applicable, block vulnerable versions | +| INC-08 | Availability outage | Present explicit unavailable/offline state, prevent ambiguous mutations, activate support/downtime path | + +### 15.3 Recovery proof + +Recovery is incomplete until: + +1. the unsafe capability is demonstrably disabled; +2. protected state, sessions, push tokens, and queued actions are reconciled; +3. wrong/stale content is corrected or retracted from server, cache, and device; +4. audit evidence is preserved with controlled access; +5. affected patients and organizations receive legally/clinically approved communication; +6. regression, penetration, and hazard tests pass on the exact remediation artifact; +7. named clinical, privacy, security, operations, and release authorities approve + restoration; and +8. the incident updates this model and its activation gates. + +The current foundation has no remote kill switch or live rollback path because it has no +live capability. Those controls must exist and be exercised before activation. + +## 16. Open residual risks and current disposition + +| Risk ID | Open risk | Current disposition | +| -------- | -------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------- | +| RISK-001 | iOS lifecycle cover does not prevent active-screen screenshot/recording | Acceptable only because no PHI is shown; security/privacy decision required before data | +| RISK-002 | Android Keystore key does not require user authentication | Dormant synthetic use only; session threat model decides future binding | +| RISK-003 | iOS protected state has no app-level reauthentication/biometric policy | Dormant synthetic use only | +| RISK-004 | Swift/Kotlin immutable strings cannot be reliably zeroized | No live composition UI; input/clipboard/keyboard design remains gated | +| RISK-005 | Android Release minification/obfuscation is disabled | No live data/network; signed-release security decision remains gated | +| RISK-006 | No root/jailbreak, attestation, overlay, hooking, or runtime-integrity policy | No live capability; risk-based device posture required | +| RISK-007 | No approved continuous dependency vulnerability/provenance response SLO | Generated inventory reduces drift ambiguity only; supply-chain program still required | +| RISK-008 | Identity, proofing, session, recovery, and representatives are undecided | Keep identity/provider/network disabled | +| RISK-009 | Authoritative current-inpatient source, reconciliation, and freshness are undecided | Keep source adapter/query disabled | +| RISK-010 | Audit, telemetry, privacy redaction, detection, and retention are undecided | No live operations or patient data | +| RISK-011 | Messaging, routing, urgent guidance, delivery semantics, and notifications are undecided | No message/push operation or provider | +| RISK-012 | No live kill switch, rollback propagation, incident SLO, or on-call model | No live capability; required before integration/pilot | +| RISK-013 | No named clinical safety officer or signed clinical-hazard review is recorded | Draft model only; no clinical activation | +| RISK-014 | No human patient, VoiceOver/TalkBack, physical-device, language/RTL, or interpreter review | Current automated shell evidence only | +| RISK-015 | No Nightingale penetration test, red-team exercise, or incident tabletop | No network/data/pilot; GATE-16/15 remain closed | +| RISK-016 | Personal-device environment can expose health-app presence and behavior outside app controls | Privacy/minimization/support review required; do not overstate confidentiality | +| RISK-017 | Release/store signing, store privacy declarations, background rights/attribution, backup/restore, upgrade, and supported-version behavior unproven | Local manifest/code/media lineage and builds only; no distribution authorization | + +## 17. Ownership and approval record + +| Discipline | Required decision | Current record | +| ------------------------ | --------------------------------------------------------------------------- | ---------------------------------------- | +| Product/contract | Scope, operation ownership, compatibility, support promise | Foundation owner only; no live operation | +| Identity/security | Provider, proofing, session, recovery, device posture, threat closure | Unassigned/not approved | +| Privacy/legal/HIM | Data flow, relationship, consent/authority, disclosure, retention, incident | Unassigned/not approved | +| Clinical safety | Hazard severity, controls, safety case, clinical incident and residual risk | Unassigned/not approved | +| Clinical/content | Field release, source, vocabulary, uncertainty, correction/retraction | Unassigned/not approved | +| Data governance/source | Authoritative source, reconciliation, freshness, lineage, quality | Unassigned/not approved | +| Nursing/medical/pharmacy | Workflow fit, routing, urgent use, patient meaning | Unassigned/not approved | +| Accessibility/language | AT, motor, cognition, reading level, translation, interpreter | Unassigned/not approved | +| Support/operations | Lost/shared device, lockout, downtime, response, kill switch, on-call | Unassigned/not approved | +| Release | Exact artifacts, signing, stores, pilot manifest, rollback, go/no-go | Unassigned/not approved | + +An approval entry must identify the person/authority, date, reviewed artifact version/SHA, +scope, conditions, expiry/re-review triggers, residual risks, and rollback relevance. + +## 18. Change-control rules + +This model must be reviewed when any of the following changes: + +- route, OpenAPI operation, server, network client, permission, entitlement, or provider; +- identity, proofing, enrollment, session, recovery, representative, or support workflow; +- principal/patient/encounter handle, authorization predicate, scope, purpose, or cardinality; +- inpatient source, cohort, status mapping, reconciliation, freshness, or cache; +- patient-visible field, vocabulary, translation, uncertainty, correction, or retraction; +- message topic, router, staff pool, delivery state, attachment, urgent guidance, or push; +- local storage, Keychain/Keystore profile, backup, device transfer, logs, analytics, or crash + reporting; +- third-party SDK, dependency, signing, store, CI, deployment, or supported OS baseline; +- privacy cover, screenshot behavior, accessibility semantics, device posture, or offline UX; +- incident, monitoring, kill-switch, rollback, retention, or support procedure; or +- material external regulation, standard, threat intelligence, incident, or patient feedback. + +Each revision must: + +1. update affected `AST`, `TB`, `CTRL`, `THR`, `HZ`, `ABUSE`, `GATE`, `VER`, `INC`, and + `RISK` entries; +2. retain superseded findings and risk decisions in the execution log; +3. link exact tests/evidence and artifact SHA; +4. state what remains disabled; +5. identify named reviewers and missing approvals; and +6. never convert a candidate or automated check into an implied clinical approval. + +## 19. Ordered implementation work + +1. Keep every existing activation field false and add this model to CI as a required, + self-tested governance artifact. +2. Obtain named clinical-safety and security owners to review severity, likelihood, + omitted hazards, and the applicability of external standards. +3. Resolve the independent identity/proofing/session/recovery/shared-device model before + building a provider adapter. +4. Resolve self-only linkage, authoritative current-inpatient source, overlap/cardinality, + freshness, contradiction, outage, and rollback before any query. +5. Define privacy-minimized audit/detection and incident events before the first patient + operation. +6. Complete the first read-only operation's authorization, release, vocabulary, + correction/retraction, offline, accessibility, and cross-patient negative matrices. +7. Perform only an approved, synthetic/deidentified, default-off non-production integration + after GATE-01 through GATE-18 evidence is versioned. +8. Defer messaging and notifications until read-only safety, urgent-use boundaries, + routing accountability, deterministic delivery semantics, and support are proven. +9. Complete signed-artifact MASVS assessment, penetration testing, red-team abuse cases, + human accessibility/language/patient review, and incident/rollback tabletops. +10. Require a separately signed, scope/expiry-bound pilot go/no-go record. Deployment, + technical success, or database connectivity must never imply patient activation. + +## 20. Non-authorization statement + +This document: + +- does not authorize production database access, a sample patient, production credentials, + or patient-data inspection; +- does not approve a route, client, provider, adapter, source query, identity, disclosure, + mutation, message, notification, integration, migration, deployment, or pilot; +- does not assert regulatory or standards compliance; +- does not accept any residual risk; +- does not replace clinical, privacy, security, legal, accessibility, language, support, or + release review; and +- does not make the current foundation safe for live inpatient use. + +Its purpose is to prevent the absence of implementation from being mistaken for the +presence of a complete safety/security design, and to make the evidence required for any +future activation explicit and testable. diff --git a/docs/nightingale/GENERIC-NON-DISCLOSURE-FOUNDATION-2026-07-27.md b/docs/nightingale/GENERIC-NON-DISCLOSURE-FOUNDATION-2026-07-27.md new file mode 100644 index 00000000..146dd937 --- /dev/null +++ b/docs/nightingale/GENERIC-NON-DISCLOSURE-FOUNDATION-2026-07-27.md @@ -0,0 +1,273 @@ +# Nightingale generic non-disclosure foundation + +**Date:** 2026-07-27 + +**Status:** Implemented, route-free safety foundation; not an operation, authorization, +clinical release, or production approval + +**Applies to:** the future Nightingale patient API boundary only + +**Does not apply to:** Hummingbird staff, the legacy Hummingbird Patient reference apps, or +any existing Zephyrus route + +## 1. Decision + +Nightingale now owns a small, pure domain gate that proves one bounded anti-enumeration +property before any patient operation exists: + +> An unknown, revoked, expired, cross-principal, wrong-encounter, omitted-resource, or +> failed-upstream-precondition request produces the same public disposition and the same +> complete public failure tuple. + +The exact public failure tuple is: + +| Field | Exact value | +| --------------- | ------------------------------ | +| `status` | `404` | +| `code` | `not_found` | +| `cache_control` | `private, no-store, max-age=0` | + +It contains no patient copy, internal reason, identifier, redirect, retry hint, source +state, relationship state, grant state, or variable field. + +Only one combination may advance: + +1. the existing identity/current-inpatient prerequisites returned + `continue_to_governed_evaluation`; +2. the request-scoped relationship is `active`; +3. the opaque request handle matches the current context; and +4. the requested resource is released. + +Advancing means only `continue_to_governed_projection_evaluation`. It is deliberately not +named or treated as authorization. Source integrity, operation policy, field release, +freshness, uncertainty, language, correction/retraction, audit-before-disclosure, and the +serialization-boundary recheck remain mandatory later gates. + +## 2. Why this is a foundation rather than a route + +The Nightingale executable OpenAPI contract still has zero paths. The repository still +registers no Nightingale route, controller, middleware, service provider binding, identity +provider, source adapter, database query, native network client, patient projection, or +mutation. Creating a live endpoint now would incorrectly collapse unresolved identity, +source, clinical-content, patient-language, privacy, accessibility, operational, and +release decisions. + +The domain gate is therefore: + +- identifier-free; +- route-free; +- framework-independent; +- side-effect-free; +- unable to query a patient source; +- unable to emit an audit event; +- unable to return patient content; and +- unreachable from any application request. + +This design permits exhaustive proof of the state-collapse invariant without implying that +the surrounding product has been authorized or implemented. + +## 3. Inputs and deliberately bounded vocabularies + +### 3.1 Existing precondition disposition + +| State | May advance? | Meaning at this gate | +| --------------------------------- | ------------ | ------------------------------------------------------------------------- | +| `withhold` | No | Identity or current-inpatient prerequisite did not pass | +| `continue_to_governed_evaluation` | Yes | Both prerequisite ports were explicitly positive; later gates still apply | + +The existing precondition gate only reaches the positive state when the independent +Nightingale identity state is `verified_self` and the current-inpatient source state is +`confirmed_current`. + +### 3.2 Relationship state + +| State | May advance? | Public distinction permitted? | +| ----------------- | ------------ | ----------------------------- | +| `unknown` | No | No | +| `active` | Yes | Not applicable | +| `revoked` | No | No | +| `expired` | No | No | +| `cross_principal` | No | No | + +These values carry no principal, patient, grant, representative, or source identifier. +They are request-scoped results expected from a future approved authorization adapter. + +### 3.3 Encounter binding state + +| State | May advance? | Public distinction permitted? | +| ------------------------- | ------------ | ----------------------------- | +| `matches_current_context` | Yes | Not applicable | +| `wrong_encounter` | No | No | + +The state is evaluated after a future adapter resolves an opaque Nightingale handle. The +domain seam does not accept or expose that handle or a source encounter identifier. + +### 3.4 Resource state + +| State | May advance? | Public distinction permitted? | +| ---------- | ------------ | ----------------------------- | +| `released` | Yes | Not applicable | +| `omitted` | No | No | + +`omitted` is intentionally the public-boundary collapse for unknown, absent, closed, +withheld, unreleased, retracted, or otherwise non-disclosable resources. The distinction +between those internal causes may be retained only as a bounded code in a future approved, +content-free audit record. It must never alter the public tuple. + +## 4. Exhaustive truth-table proof + +The implementation evaluates the Cartesian product: + +- 2 precondition dispositions; +- 5 relationship states; +- 2 encounter-binding states; and +- 2 resource states. + +That produces exactly 40 combinations: + +| Outcome | Combination count | +| -------------------------------------------- | ----------------- | +| `continue_to_governed_projection_evaluation` | 1 | +| `withhold_not_found` | 39 | +| Total | 40 | + +The sole continuing row is: + +| Preconditions | Relationship | Encounter binding | Resource | Result | +| --------------------------------- | ------------ | ------------------------- | ---------- | ------------------------------------------------ | +| `continue_to_governed_evaluation` | `active` | `matches_current_context` | `released` | Continue to later governed projection evaluation | + +Every other permutation returns `withhold_not_found` and the exact three-field public +failure tuple. + +### 4.1 Named plan cases + +The PHPUnit suite contains an explicit, readable case for each plan requirement: + +| Required case | Differing input | Exact public result | +| ---------------- | ----------------------------------- | ------------------- | +| Unknown | relationship `unknown` | Generic 404 tuple | +| Revoked | relationship `revoked` | Generic 404 tuple | +| Expired | relationship `expired` | Generic 404 tuple | +| Cross-principal | relationship `cross_principal` | Generic 404 tuple | +| Wrong encounter | encounter binding `wrong_encounter` | Generic 404 tuple | +| Omitted resource | resource `omitted` | Generic 404 tuple | +| Upstream denial | preconditions `withhold` | Generic 404 tuple | + +The independent CI verifier repeats the full 40-row truth table rather than trusting the +PHPUnit assertions alone. It also pins the exact enum vocabularies, the one/39 +cardinalities, and the public tuple. + +## 5. Evaluation order and race boundary + +The implemented order is: + +1. prerequisite disposition; +2. relationship eligibility; +3. current-context binding; +4. resource release eligibility; and +5. continue or generically withhold. + +The order is not intended to expose which check failed. No caller exists, and the returned +value has no reason field. When a live operation is later proposed, it must add: + +- equivalent material work or another reviewed timing-control strategy for existence- + sensitive equivalence classes; +- rate-limit and abuse controls that do not become an oracle; +- a fresh authorization and source-lifecycle recheck immediately before serialization; +- atomic response withholding if the context, relationship, resource, or release changes; +- no-store/no-index response enforcement at the real HTTP edge; and +- response-length, header, redirect, retry, and cache equivalence tests. + +Those runtime properties are not claimed by this route-free foundation. + +## 6. Audit boundary + +The gate does not log. This is intentional: introducing an audit writer before event +ownership, retention, access control, availability behavior, and schema are approved would +create an ungoverned patient-security data set. + +A future operation must separate: + +- a stable public code (`not_found`); +- an internal bounded diagnostic reason code; +- a durable, content-free request evaluation event where required; +- a durable, content-free disclosure event before successful content release; and +- operational diagnostics that contain no raw handle, source identifier, patient value, + staff actor, free text, credential, or stack material. + +A denial-audit outage must not make known resources behave differently from unknown ones. +An audit-before-disclosure outage must fail closed. + +## 7. Source inventory + +| Source | Responsibility | +| -------------------------------------------------------------------- | ------------------------------------------------------------------------------------------- | +| `app/Nightingale/Disclosure/NightingaleRelationshipState.php` | Exact relationship vocabulary; only `active` may advance | +| `app/Nightingale/Disclosure/NightingaleEncounterBindingState.php` | Exact current-context match vocabulary | +| `app/Nightingale/Disclosure/NightingaleResourceState.php` | Exact released/omitted resource vocabulary | +| `app/Nightingale/Disclosure/NightingaleDisclosureDisposition.php` | Exact generic public failure tuple and non-authorizing continue disposition | +| `app/Nightingale/Disclosure/NightingaleGenericNonDisclosureGate.php` | Pure all-positive conjunction and generic collapse | +| `tests/Unit/Nightingale/NightingaleBackendFoundationTest.php` | Named plan cases and complete 40-row truth table | +| `scripts/ci/verify-nightingale-backend-foundation.php` | Dependency-free independent vocabulary, tuple, cardinality, and runtime-registration checks | + +No Hummingbird namespace, legacy patient identifier, route, controller, data model, source +schema, framework response, or database dependency is imported. + +## 8. Verification + +Run from the repository root: + +```bash +./vendor/bin/pint --test \ + app/Nightingale/Disclosure \ + tests/Unit/Nightingale/NightingaleBackendFoundationTest.php \ + scripts/ci/verify-nightingale-backend-foundation.php + +php artisan test --filter=NightingaleBackendFoundationTest + +php scripts/ci/verify-nightingale-backend-foundation.php --self-test + +node scripts/ci/verify-nightingale-contract-foundation.mjs --self-test +node scripts/ci/verify-nightingale-encounter-access-candidate.mjs --self-test +node scripts/ci/verify-nightingale-today-candidate.mjs --self-test +``` + +The test suite must prove: + +- 21 PHPUnit cases and 111 assertions for the current combined backend foundation; +- all seven named non-disclosure cases return the same disposition and public tuple; +- exactly one of the 40 disclosure permutations continues; +- the other 39 permutations withhold; +- the unconfigured identity/source ports still fail closed; +- no Nightingale route or runtime binding exists; and +- the executable contract still contains zero paths and all activation fields remain + false. + +Counts are evidence for the current source, not a waiver for reviewing future test changes. + +## 9. What this completion does and does not satisfy + +This implementation satisfies the bounded Stream D checklist requirement to prove generic +non-disclosure for unknown, revoked, expired, cross-principal, wrong-encounter, and +omitted-resource requests at the route-free Nightingale domain boundary. + +It does not satisfy or approve: + +- a Nightingale identity provider or recovery flow; +- an encounter/source adapter or production query; +- an opaque-handle generator, store, rotation policy, or collision design; +- a patient route, controller, middleware chain, or native API client; +- clinical-content or patient-language release; +- Today, My Path, Care Team, education, discharge, communication, or representative access; +- audit storage, monitoring, timing-equivalence, rate-limit, load, penetration, or + production-like integration evidence; +- a real HTTP response or client error-state mapping; +- patient, clinical-safety, accessibility, privacy/security, language, legal/HIM, + operational, or release approval; +- pilot enrollment, distribution, production activation, or deployment; or +- production database access or mutation. + +Any later operation must preserve this state-collapse property and add operation-specific +authorization, audit, race, serialization, client, and HTTP-edge proof before the +executable contract gains a path. diff --git a/docs/nightingale/IDENTITY-INPUT-FIRST-READ-ERROR-SOURCE-CLASSIFICATION-2026-07-26.md b/docs/nightingale/IDENTITY-INPUT-FIRST-READ-ERROR-SOURCE-CLASSIFICATION-2026-07-26.md new file mode 100644 index 00000000..764ffe15 --- /dev/null +++ b/docs/nightingale/IDENTITY-INPUT-FIRST-READ-ERROR-SOURCE-CLASSIFICATION-2026-07-26.md @@ -0,0 +1,698 @@ +# Nightingale identity input, enrollment/recovery, first-read, and error source classification + +**Status:** Complete evidence classification for this bounded migration slice; no +implementation approval + +**Reviewed source commit:** `b1078405de2dacd767ec69da11197f1e755d8277` + +**Normative machine-readable ledger:** +[`migration/candidates/v0/source-classification.json`](./migration/candidates/v0/source-classification.json) + +**Mechanical verifier:** `scripts/ci/verify-nightingale-source-classification.mjs` + +## 1. Executive determination + +Sixty-five exact legacy contract, backend, database, iOS, Android, and test files were +reviewed and pinned by SHA-256. The result is intentionally conservative: + +1. **Nightingale still has no approved identity input.** Email/password, invitation UUID, + invitation token, verification code, display name, local account password, durable device + UUID, and legacy access/refresh tokens remain Hummingbird decisions. None is approved for + Nightingale. +2. **The repository does not contain a complete patient account-recovery workflow.** The + database accepts an `account_recovery` challenge purpose and native decoders recognize a + recovery auth-method label, but there is no patient recovery route, request contract, + proofing service, UI, completion transaction, session invalidation policy, or recovery + acceptance suite. Schema vocabulary is not functionality. +3. **Both native clients make an unsafe first-read decision.** iOS uses + `encounters.data.encounters.first`; Android uses `firstOrNull()`. Backend ordering by + `valid_from DESC` and grant UUID is neither a patient choice nor a clinical reconciliation + rule. Nightingale continues to require zero or one verified current self context and + returns a governed review state when more than one is eligible. +4. **The legacy server has valuable anti-disclosure controls, but they do not constitute a + Nightingale contract.** Central safe-error mapping, generic projection `404`, request-time + ownership checks, no-store headers, content allowlists, and audit-before-disclosure are + principles to reimplement. The Hummingbird route, realm, scopes, schemas, error codes, + English copy, and release policies remain product-specific. +5. **Native error behavior is materially weaker and inconsistent.** iOS can render server + messages directly. Android can render the server message for HTTP 422. iOS collapses + projection `404` to absence, while Android collapses both `403` and `404`. Both collapse + incompatible patient-state vocabulary to absence. These paths can misrepresent withheld, + unavailable, incompatible, and genuinely empty information as the same patient state. +6. **No source in this classification is approved for runtime adoption.** The manifest + mechanically pins all implementation, route, provider, credential migration, first-row + selection, server-message passthrough, projection-absence conflation, production query, + production replay, and patient/principal creation permissions to `false`. + +This milestone completes source classification, not identity ratification, API +implementation, clinical release, pilot authorization, production data validation, or +deployment. + +## 2. Scope and method + +### 2.1 In scope + +The review covers four connected domains: + +- identity data collected or decoded by a patient client; +- enrollment, recovery, session establishment, refresh, and revocation; +- the first read that selects an inpatient context and fans out to patient projections; and +- patient-facing errors, anti-enumeration, non-disclosure, absence, incompatibility, and + unavailable-state handling. + +The evidence set includes: + +| Surface | Files | Purpose | +| --------------------------- | -----: | ---------------------------------------------------------------------- | +| Legacy contract | 1 | Request/response/security/error compatibility input | +| Legacy backend and database | 33 | Routes, gates, controllers, models, policies, services, and migrations | +| Backend tests | 8 | Existing guarantees and missing negative cases | +| Legacy iOS and tests | 11 | Input lifecycle, transport, models, first-read projection, error UX | +| Legacy Android and tests | 12 | Input lifecycle, transport, models, first-read projection, error UX | +| **Total** | **65** | Every file is independently classified and checksum-pinned | + +Category membership overlaps by design. Counts are: + +| Category | Classified files | +| ------------------------ | ---------------: | +| Identity input | 44 | +| Enrollment and recovery | 37 | +| First-read projection | 47 | +| Error and non-disclosure | 65 | + +### 2.2 Out of scope + +This review did not: + +- connect to or query a production database; +- create a patient, principal, identity link, grant, session, challenge, or projection; +- inspect real patient, encounter, credential, or source-system data; +- approve an identity provider, source adapter, facility, unit, cohort, or relationship; +- register `/api/nightingale/v1` or any operation beneath it; +- generate a network client; +- copy a Hummingbird model, route, schema, credential, storage key, or error code; +- approve patient copy, translations, recovery support, or a clinical projection; +- deploy any code; or +- change the legacy Hummingbird patient application. + +### 2.3 Classification vocabulary + +| Disposition | Meaning | +| ---------------------------- | ------------------------------------------------------------------------------------------------------------------------------ | +| `reimplement_principle_only` | A narrow safety property may inform new Nightingale code after approval; no source code or product decision is reusable as-is | +| `evidence_only` | The source records existing behavior or test technique; it is neither an implementation requirement nor an approval | +| `held` | The behavior depends on unresolved identity, clinical, privacy, source, contract, accessibility, support, or release decisions | +| `reject` | The behavior conflicts with the independent product boundary and must not migrate | + +The classification is not a code-reuse license. Even a +`reimplement_principle_only` source remains prohibited from runtime adoption by this +milestone. + +## 3. End-to-end behavior traced + +### 3.1 Legacy identity and enrollment path + +```text +native email/password + -> POST /api/patient/v1/auth/token + -> Hummingbird product + token-exchange gates + -> lower-case email lookup + -> password check, with a dummy hash for unknown accounts + -> active/unlocked principal check + -> Hummingbird patient session row + -> Sanctum access + refresh bearer tokens + -> native legacy credential store + +native invitation UUID + invitation token + code + name + email + password + -> POST /api/patient/v1/auth/enroll/challenge/verify + -> Hummingbird product + enrollment gates and throttle + -> row-lock challenge + -> verify challenge and code hashes + -> row-lock grant, identity link, and principal + -> activate local principal and grant + -> consume challenge atomically + -> issue Hummingbird session and bearer tokens +``` + +Useful properties include comparable unknown-account password work, bounded inputs, row +locking, atomic challenge consumption, one-way secret hashes, failure counters, refresh +rotation, refresh-reuse response, local-store failure cleanup, session revocation, and +auditing. + +The path is not suitable for Nightingale because: + +- the identity provider is an unapproved local email/password account; +- invitation issuance and delivery authority are outside the patient API and not governed as + a complete operating workflow; +- the client collects seven enrollment fields and makes local password/shape decisions; +- device identity and metadata are part of the legacy session contract; +- representative enrollment and identity transition behavior are incomplete; +- account recovery is not implemented; +- Hummingbird scopes, routes, models, token names, and storage keys cross the boundary; and +- no Nightingale-specific assurance, correlation, support, or revocation acceptance decision + exists. + +### 3.2 Legacy first-read path + +```text +access token + -> GET legacy profile and encounter grants + -> server filters active owned grants + -> server sorts valid_from DESC, then grant UUID + -> client silently takes first row + -> client reads raw grant scopes + -> client concurrently requests Today, Pathway, Pathway Events, + Discharge Readiness, Rounds Summary, and Care Team + -> client converts not-found/incompatible responses to nil + -> snapshot fabricates or infers local presentation state +``` + +The first list endpoint does not prove that a source encounter is authoritatively current. +It proves only that a legacy grant row passed legacy status, effective-time, relationship, +and policy checks. Its `source_freshness.status = current` claim is derived from the presence +of grant rows, not source health or an admission-state observation. + +The clients then compound that uncertainty: + +- both choose the first row without a patient or clinical rule; +- both use raw scopes to decide which server endpoints to call; +- neither requires a Nightingale-owned current-inpatient source result; +- neither requires a verified, unmerged, same-principal identity link at the list boundary; +- both map projection failures to nullable local values; +- both withhold incompatible state vocabulary without an explicit incompatibility state; +- iOS can generate fresh random UUIDs when three server identifiers are malformed; and +- fallback copy can imply “no released update” or “no active stay” without distinguishing a + complete authoritative empty evaluation from dependency failure. + +### 3.3 Legacy error path + +```text +controller/service/framework failure + -> patient response decorator + -> allowlisted legacy code or status-derived fallback + -> server-owned English message + -> no-store/no-index response + -> native decoder + -> native product-specific mapping +``` + +The backend decorator correctly discards arbitrary framework messages for covered patient +routes and maps errors through an allowlist. It also adds no-store, no-index, and +authorization variance headers. Those are strong candidate controls. + +The end-to-end contract still has gaps: + +- `AuthController` constructs responses from internal failure messages before the outer + decorator repairs them; the safety property therefore depends on complete, correctly + ordered middleware and exception coverage. +- Validation responses preserve per-field error detail. That may be appropriate, but fields, + localization, enumeration risk, assistive-technology behavior, and sensitive values need a + Nightingale decision. +- iOS `PatientFacingError` returns `.server` and `.unauthorized` messages directly. +- Android's generic auth mapping still returns `PatientApiException.message` for HTTP 422. +- iOS recognizes only projection `404` as nullable absence; Android also recognizes `403`. +- A vocabulary-version mismatch becomes `nil` on both platforms. +- A missing client scope becomes `nil` without consulting a patient-facing capability + contract. +- A projection failure other than the locally swallowed states can fail the broader + first-load operation; iOS uses a single concurrent tuple and Android loads the bundle + synchronously. +- Neither client has a complete state model separating `not_released`, `not_authorized`, + `temporarily_unavailable`, `contract_incompatible`, `source_stale`, `account_review`, and + `authoritative_empty`. + +## 4. Platform parity and delta + +| Concern | Backend reference | iOS reference | Android reference | Nightingale decision | +| ------------------------------- | --------------------------------------------------------------------------- | --------------------------------------------------------------- | -------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------- | +| Primary sign-in | Local lower-cased email + password | Email + password | Email + password | **Reject.** Provider and assurance are unapproved | +| Enrollment input | Challenge UUID/token/code + name/email/password + optional device | Same seven human-entered fields | Same seven fields | **Hold.** Redesign from approved proofing and recovery policy | +| Secret input lifetime | Request-scoped; hashes persisted for challenge material | SwiftUI `@State`; no explicit background clear in this view | Compose volatile state; clears selected secrets after submit, not a complete lifecycle proof | **Reject current flows.** Future volatile input must clear on background, identity change, sign-out, recovery, and completion | +| Unknown credential handling | Dummy password hash and generic invalid-credentials result | Displays server message through generic auth error mapping | Maps 401 generically | Reimplement constant-work and anti-enumeration principles; client renders owned localized copy | +| Inactive/locked account | Distinct allowlisted codes and statuses | May render server message | Mostly status-derived generic copy | Needs one approved enumeration and support policy | +| Recovery | Challenge schema enum only; no patient route/service | Decoder enum only; no recovery UI | Decoder enum only; no recovery UI | **Not implemented.** Do not infer parity from enum/schema presence | +| Token/session | Sanctum access/refresh family, rotation, revocation, legacy device metadata | Persists legacy access/refresh tokens in Hummingbird Keychain | Persists legacy access/refresh/session and durable device UUID | **Reject migration.** Independent provider, credentials, session model, and storage descriptor required | +| Encounter list | Returns seven grant-derived fields ordered by `valid_from DESC`, then UUID | Chooses `.first` | Chooses `.firstOrNull()` | **Reject first-row selection.** Zero/one self context only; multiple is governed review | +| Current inpatient truth | Not proved by encounter list; grant-row update time labeled current | Trusts returned row | Trusts returned row | Require an approved authoritative source port with explicit unavailable/inconsistent/closed/current states | +| Client authorization | Server reauthorizes projections | Raw scopes control request fan-out | Raw scopes control request fan-out | Client scopes never authorize; future capability hints must be separately owned and server always reauthorizes | +| Unknown/cross-principal project | Generic `404` | `404` becomes nil | `403` and `404` become nil | Server anti-oracle response plus explicit client-safe disposition; platform behavior must be identical | +| Unreleased projection | Generic `404` | Becomes nil | Becomes nil | Do not represent as a clinical empty state; use approved generic unavailable/withheld presentation | +| Vocabulary incompatibility | Version sent in envelope; backend validates configured codes | Incompatible version becomes nil | Incompatible version becomes nil | Fail closed with a distinct contract-incompatible state, telemetry without PHI, safe retry/update guidance, and no inferred text | +| Malformed projection UUID | Backend emits stored UUID | Some item/stage/member UUIDs become new random UUIDs | Decoder generally retains strings with bounded validation in selected paths | Reject whole affected projection; never invent a server identity | +| Server error message | Central decorator emits allowlisted English copy | `.server` and `.unauthorized` message can be displayed verbatim | HTTP 422 can display decoded server message | No server-message passthrough; map stable Nightingale codes to local approved localized copy | +| Validation detail | Preserves `errors` field | Decoded through legacy envelope | Decoded through legacy envelope | Exact safe fields, ordering, localization, announcement, and value suppression require approval | +| No-store | Applied by envelope/decorator | Ephemeral URLSession policy is tested | Cacheless/no-cache transport policy is tested | Reimplement and verify on every success, error, redirect, background, and OS cache path | +| Audit before disclosure | Projection success requires durable audit; denials are best effort | No independent receipt validation | No independent receipt validation | Retain server-side durable audit principle; define failure semantics and correlation contract | + +## 5. Source-by-source classification + +The ledger is normative for exact hashes and full decisions. The tables below provide a +human-reviewable index so that no grouped statement conceals an unreviewed file. + +### 5.1 Contract, route, configuration, and database + +| Source | Disposition | Decisive finding | +| ----------------------------------------------------------------------------------------- | ------------- | -------------------------------------------------------------------------------------------------------- | +| `docs/hummingbird/api-contract/hummingbird-patient.v1.yaml` | Evidence only | Inventory all request/response/security/error shapes; copy no operation or schema | +| `routes/patient.php` | Reject | Every path, name, middleware stack, feature flag, and token ability is Hummingbird-owned | +| `config/hummingbird-patient.php` | Reject | Legacy environment flags, token policy, copy, source keys, and policy versions are not Nightingale gates | +| `database/migrations/2026_07_19_000100_create_patient_experience_identity_foundation.php` | Held | Useful constraints coexist with unapproved principals, purposes, relationships, sessions, and linkage | +| `database/migrations/2026_07_19_000200_create_patient_experience_projection_kernel.php` | Held | Release/provenance concepts require an independently approved Nightingale schema | + +### 5.2 Backend boundary and controllers + +| Source | Disposition | Decisive finding | +| -------------------------------------------------------------------- | -------------------------- | ------------------------------------------------------------------------------------------------------------ | +| `app/Http/Concerns/RendersPatientEnvelope.php` | Reimplement principle only | Exact envelope and no-store behavior are useful; legacy metadata ownership is not | +| `app/Http/Controllers/Api/Patient/AuthController.php` | Held | Bounded failure handling is evidence; legacy enrollment/password/refresh paths are held | +| `app/Http/Controllers/Api/Patient/EncounterController.php` | Held | Request-time checks and audit help; seven grant fields and grant-derived freshness overexpose internals | +| `app/Http/Controllers/Api/Patient/EncounterProjectionController.php` | Reimplement principle only | Generic not-found collapse is useful; projection kinds, paths, scopes, and service coupling remain legacy | +| `app/Http/Controllers/Api/Patient/MeController.php` | Reject | Profile response exposes identity/contact/verification/preference fields without Nightingale field ownership | +| `app/Http/Controllers/Api/Patient/SessionController.php` | Reimplement principle only | Owned revocation and unknown/cross-principal equivalence help; session/device contract does not | +| `app/Http/Requests/Patient/TokenRequest.php` | Reject | Email/password and durable-device input are unapproved identity-provider choices | +| `app/Http/Requests/Patient/VerifyEnrollmentChallengeRequest.php` | Held | Bounded two-part proof is evidence; enrollment shape, password, and device decisions require redesign | +| `app/Http/Middleware/EnsureHummingbirdPatientEnabled.php` | Reimplement principle only | Product-default-off is required, but the Hummingbird flag cannot authorize Nightingale | +| `app/Http/Middleware/EnsureHummingbirdPatientFeatureEnabled.php` | Reimplement principle only | Per-operation kill switches are required, with Nightingale-owned operations and approvals | +| `app/Http/Middleware/EnsurePatientRealm.php` | Reimplement principle only | Exact realm/session ownership is useful evidence; provider, tokens, model, and errors must be independent | +| `app/Http/Middleware/ProtectPatientResponse.php` | Reimplement principle only | Central decoration is useful only with independent route coverage and a Nightingale safe-error registry | + +### 5.3 Backend identity, access, projection, and error sources + +| Source | Disposition | Decisive finding | +| ------------------------------------------------------------------------ | -------------------------- | ------------------------------------------------------------------------------------------------------------------------ | +| `app/Models/Patient/PatientPrincipal.php` | Reject | Local Hummingbird account/password and profile fields cannot migrate | +| `app/Models/Patient/PatientIdentityLink.php` | Held | Encrypted/digested linkage is useful; authority, assurance, merge, and revocation lifecycle are unresolved | +| `app/Models/Patient/PatientEnrollmentChallenge.php` | Held | Hashing, expiry, attempts, consumption, and revocation help; default enrollment purpose is not recovery governance | +| `app/Models/Patient/PatientSession.php` | Held | Session-family lifecycle is evidence; token/device/network metadata and migration are rejected | +| `app/Models/Patient/PatientEncounterAccessGrant.php` | Held | Ownership/effective state helps; raw scopes, relationship defaults, linkage, and nullability are unresolved | +| `app/Models/Patient/PatientEncounterProjection.php` | Held | Immutable release concepts help; kinds, schema, content, scope, and release state need independent ownership | +| `app/Models/Patient/PatientReleasePolicyVersion.php` | Held | Versioned effective policy is required; no legacy row or environment selection is approval | +| `app/Policies/Patient/PatientEncounterAccessGrantPolicy.php` | Held | Defense in depth lacks identity-link assurance and authoritative current-source gates | +| `app/Policies/Patient/PatientEncounterProjectionPolicy.php` | Held | Policy remains coupled to legacy grants, scopes, relationships, releases, and version selection | +| `app/Services/Patient/PatientAuthFailure.php` | Reimplement principle only | Typed internal codes help; internal messages must never be serialized directly | +| `app/Services/Patient/PatientAuthService.php` | Held | Atomic proof/session controls help; local accounts, device fingerprinting, legacy models, and missing recovery block use | +| `app/Services/Patient/PatientEncounterAccessService.php` | Held | Filtering helps; ordering enables unsafe first-row choice and lacks identity/current-source proof | +| `app/Services/Patient/PatientResponseDecorator.php` | Reimplement principle only | Allowlists and header controls help; legacy codes, raw field errors, English copy, and coverage need review | +| `app/Services/Patient/PatientResponseMetadata.php` | Held | Request/freshness/policy/version metadata helps; aliases and default claims are unapproved | +| `app/Services/Patient/Projection/PatientProjectionContentGuard.php` | Reimplement principle only | Allowlist-first schema and forbidden-source checks are strong; exact patient content contract remains held | +| `app/Services/Patient/Projection/PatientProjectionDisclosureService.php` | Reimplement principle only | Reauthorization, anti-oracle collapse, release filter, and audit-before-payload help; legacy models do not | +| `app/Services/Patient/Projection/PatientProjectionStateVocabulary.php` | Held | Code validation helps; registry, version, labels, locale, and free text are not approved | + +### 5.4 Backend tests + +| Source | Disposition | Decisive finding | +| --------------------------------------------------------- | ------------- | ------------------------------------------------------------------------------------------------ | +| `tests/Feature/Patient/PatientApiBoundaryTest.php` | Evidence only | Reuse default-off, realm, response, and forbidden-field negative-test techniques | +| `tests/Feature/Patient/PatientAuthLifecycleTest.php` | Evidence only | Use atomic proof, lockout, generic denial, rotation/reuse, cleanup, and audit cases as a catalog | +| `tests/Feature/Patient/PatientIdentityFoundationTest.php` | Evidence only | Hash/encryption/handle/append-only tests help; a recovery enum is not a recovery workflow | +| `tests/Feature/Patient/PatientProjectionApiTest.php` | Evidence only | Anti-oracle and no-store cases help; legacy projection fixtures are not accepted content | +| `tests/Feature/Patient/PatientProjectionKernelTest.php` | Evidence only | Database/content-guard negative techniques help after Nightingale schema approval | +| `tests/Feature/Patient/PatientSessionManagementTest.php` | Evidence only | Cross-principal equivalence, repeat revocation, and bounded-session cases help | +| `tests/Feature/Patient/PatientTimestampContractTest.php` | Evidence only | UTC/RFC 3339 and daylight-saving cases are reusable after field ownership | +| `tests/Unit/Patient/PatientResponseMetadataTest.php` | Evidence only | Reveals default/alias behavior; rewrite for an exact Nightingale envelope | + +### 5.5 iOS sources + +| Source | Disposition | Decisive finding | +| ----------------------------------------------------------------------------------------------- | ----------- | ------------------------------------------------------------------------------------------------------------------- | +| `hummingbird/iosPatientApp/HummingbirdPatient/Features/Authentication/PatientWelcomeView.swift` | Reject | Seven-field invitation and email/password UX are unapproved; secret state lacks explicit background clearing | +| `hummingbird/iosPatientApp/HummingbirdPatient/App/PatientAppViewModel.swift` | Reject | First-row, scope fan-out, absence/vocabulary collapse, tuple failure, and server-message passthrough are unsafe | +| `hummingbird/iosPatientApp/HummingbirdPatient/Networking/PatientAPIClient.swift` | Reject | Legacy host/path/operations/bearers/redirects/errors cannot cross the product boundary | +| `hummingbird/iosPatientApp/HummingbirdPatient/Networking/PatientAPIModels.swift` | Reject | Legacy tokens, identity, grants, projections, errors, enrollment, device, and recovery enums are not a contract | +| `hummingbird/iosPatientApp/HummingbirdPatient/Networking/PatientSecureStore.swift` | Reject | Legacy Keychain service and token schema do not migrate; only a token-agnostic primitive was independently reissued | +| `hummingbird/iosPatientApp/HummingbirdPatient/Models/PatientExperienceSnapshot.swift` | Reject | First-record selection, invented UUIDs, inferred empty states, raw scopes, and fallback source copy are prohibited | +| `hummingbird/iosPatientApp/HummingbirdPatient/Models/PatientStateVocabulary.swift` | Held | Registry, version, missing/unknown behavior, localization, and approvals are incomplete | + +### 5.6 iOS tests + +| Source | Disposition | Decisive finding | +| ---------------------------------------------------------------------------------- | ------------- | -------------------------------------------------------------------------------------------------- | +| `hummingbird/iosPatientApp/HummingbirdPatientTests/PatientAPIBoundaryTests.swift` | Evidence only | Reuse allowlist, HTTPS, host, identifier, redirect, storage, and boundary negative-test techniques | +| `hummingbird/iosPatientApp/HummingbirdPatientTests/PatientAPIClientTests.swift` | Evidence only | Transport/header/token/error cases help; every asserted legacy operation is non-authoritative | +| `hummingbird/iosPatientApp/HummingbirdPatientTests/PatientAPIModelTests.swift` | Evidence only | Malformed envelope, timestamp, field, vocabulary, and decoder cases are a useful catalog | +| `hummingbird/iosPatientApp/HummingbirdPatientTests/PatientAppViewModelTests.swift` | Evidence only | Test doubles document current first-read/error behavior and gaps, not desired Nightingale outcomes | + +### 5.7 Android sources + +| Source | Disposition | Decisive finding | +| -------------------------------------------------------------------------------------------------------------------- | ----------- | ------------------------------------------------------------------------------------------------------------------------ | +| `hummingbird/androidPatientApp/app/src/main/java/net/acumenus/hummingbird/patient/PatientAppViewModel.kt` | Reject | Legacy identity orchestration, copy, local assurance, scope-derived availability, and error policy do not migrate | +| `hummingbird/androidPatientApp/app/src/main/java/net/acumenus/hummingbird/patient/PatientExperienceModels.kt` | Reject | Legacy auth mode, enrollment fields, raw scopes, destinations, and projection models are not Nightingale models | +| `hummingbird/androidPatientApp/app/src/main/java/net/acumenus/hummingbird/patient/data/PatientApiClient.kt` | Reject | Legacy 23-path allowlist, host, bearer/device/enrollment contract, and decoded server messages are prohibited | +| `hummingbird/androidPatientApp/app/src/main/java/net/acumenus/hummingbird/patient/data/PatientApiModels.kt` | Reject | Legacy token/profile/grant/projection/error/device/session/enrollment/recovery models are not reusable | +| `hummingbird/androidPatientApp/app/src/main/java/net/acumenus/hummingbird/patient/data/PatientSecureStore.kt` | Reject | EncryptedSharedPreferences, legacy token/session schema, durable device UUID, alias, and migration are prohibited | +| `hummingbird/androidPatientApp/app/src/main/java/net/acumenus/hummingbird/patient/data/PatientSessionCoordinator.kt` | Reject | FirstOrNull, scope fan-out, 403/404 collapse, vocabulary nulling, refresh persistence, and fallback inference are unsafe | +| `hummingbird/androidPatientApp/app/src/main/java/net/acumenus/hummingbird/patient/data/PatientStateVocabulary.kt` | Held | One checksum-bound cross-platform registry, localization, exact incompatibility behavior, and review are missing | +| `hummingbird/androidPatientApp/app/src/main/java/net/acumenus/hummingbird/patient/ui/PatientAuthenticationScreen.kt` | Reject | Seven-field invitation and email/password UX are unapproved; post-submit clearing does not prove lifecycle clearing | + +### 5.8 Android tests + +| Source | Disposition | Decisive finding | +| ------------------------------------------------------------------------------------------------------------------------ | ------------- | ------------------------------------------------------------------------------------------ | +| `hummingbird/androidPatientApp/app/src/test/java/net/acumenus/hummingbird/patient/PatientAppViewModelTest.kt` | Evidence only | Reuse state, secret-clearing, generic-error, and disabled-network test techniques | +| `hummingbird/androidPatientApp/app/src/test/java/net/acumenus/hummingbird/patient/data/PatientEndpointBoundaryTest.kt` | Evidence only | Reuse exact operation, HTTPS, host, identifier, and disabled-network negative tests | +| `hummingbird/androidPatientApp/app/src/test/java/net/acumenus/hummingbird/patient/data/PatientEnvelopeDecoderTest.kt` | Evidence only | Reuse malformed envelope, UUID, length, time, vocabulary, revision, and bounded-list cases | +| `hummingbird/androidPatientApp/app/src/test/java/net/acumenus/hummingbird/patient/data/PatientSessionCoordinatorTest.kt` | Evidence only | Refresh, cleanup, first-read, absence, and vocabulary cases form a gap catalog | + +## 6. Detailed decision: identity input + +### 6.1 Inputs rejected as Nightingale defaults + +The following legacy values are not approved Nightingale inputs: + +- email address as the primary subject locator; +- a locally verified password; +- invitation/challenge UUID; +- invitation bearer token; +- short verification code; +- patient-entered display name; +- password and password confirmation during enrollment; +- client-created durable device UUID; +- device name, app version, and OS version as identity/session fields; +- Hummingbird access token; +- Hummingbird refresh token; +- Hummingbird session UUID; +- staff or legacy patient identity; +- raw grant UUID or raw encounter UUID; and +- Hummingbird relationship or scope codes. + +Rejecting a default does not mean the field can never exist. It means a named identity owner, +privacy/security owner, legal/HIM owner, accessibility/language owner, support owner, and +product owner must define why it exists, who is authoritative, how it is verified, how it is +corrected, what is retained, what is displayed, how it is localized, and how it fails without +enumeration. + +### 6.2 Minimum decision required before identity implementation + +The identity decision must define: + +1. authoritative issuer, audience, client, realm, and provider; +2. initial and step-up assurance levels by operation; +3. patient and representative subject types; +4. relationship proof, expiry, revocation, merge, and dispute behavior; +5. account discovery and enumeration resistance; +6. credential binding and device-replacement behavior; +7. session creation, rotation, theft/reuse response, idle expiry, absolute expiry, risk hold, + and logout; +8. support-assisted escalation that cannot bypass proofing; +9. recovery proof, old-binding invalidation, notification, cooling-off, and fraud response; +10. field-level profile ownership and minimization; +11. audit fields and prohibited audit content; +12. localization, accessibility, and patient-safe copy; +13. production key ownership and rotation; +14. non-production isolation and synthetic test identities; and +15. rollback that disables access without orphaning or silently widening relationships. + +Until those decisions are approved, `config/nightingale.php` must keep the provider `null` +and identity disabled. + +## 7. Detailed decision: enrollment and recovery + +### 7.1 What exists + +The reference implementation contains a reasonably defensive encounter-enrollment +transaction: + +- the challenge row is locked; +- challenge token and verification code are checked against one-way hashes; +- usability, expiry, failed attempts, lockout, consumption, and revocation are evaluated; +- grant, identity link, and principal rows are locked; +- principal/link/grant ownership consistency is checked; +- principal activation, grant activation, challenge consumption, session issue, and audit + occur within the governed flow; and +- invalid challenge cases collapse to one legacy error. + +These are evidence for future invariant design. + +### 7.2 What does not exist + +No complete recovery feature exists. Specifically, there is no: + +- Nightingale or Hummingbird patient recovery route; +- recovery request/response schema; +- recovery-start endpoint; +- recovery-completion endpoint; +- recovery proofing provider; +- recovery delivery service; +- recovery UI on iOS or Android; +- lost-device flow; +- change-email or change-phone proof; +- old-credential and old-session invalidation transaction; +- recovery replay or race policy; +- notification-to-prior-channel behavior; +- cooldown or fraud-review behavior; +- support escalation boundary; +- representative recovery policy; +- recovery audit acceptance matrix; or +- emulator end-to-end recovery test. + +The `account_recovery` database check value and `.recovery`/`"recovery"` decoder values only +reserve vocabulary. They must not be represented as implemented functionality or migration +readiness. + +### 7.3 Enrollment/recovery result + +The whole feature remains **held**. Future work may reimplement row locking, one-way secret +storage, attempt bounds, atomic consumption, session invalidation, and anti-enumeration after +the identity decision. It must not copy the legacy route, local account, challenge material, +purpose codes, delivery methods, model rows, fixtures, or patient copy. + +## 8. Detailed decision: first-read projection + +### 8.1 Required Nightingale sequence + +The initial Nightingale read remains: + +```text +approved operation in active independent contract + -> default-off product/operation/facility/cohort gates + -> verified Nightingale principal and owned session at required assurance + -> verified, current, unmerged identity link owned by principal + -> self relationship only + -> approved inpatient purpose + -> active, non-revoked, effective authorization + -> authoritative current-inpatient source result + -> zero eligible contexts: authoritative empty + -> one eligible context: disclose one Nightingale handle + -> more than one: account state requires review + -> recheck before serialization + -> durable evaluation audit + -> durable disclosure audit when a handle is returned + -> exact no-store response +``` + +No projection is requested until this context decision succeeds. + +### 8.2 Projection disposition must be explicit + +Each future projection read must return or internally preserve one of these independently +defined states: + +| Disposition | Meaning | Patient presentation requirement | +| ------------------------- | --------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------ | +| `released_current` | Approved content exists and passed all authorization, source, policy, and release gates | Render exact approved projection with freshness, provenance, and uncertainty | +| `released_stale` | Released content is still eligible but source freshness exceeded its approved threshold | Render only if policy allows, with approved stale guidance | +| `not_released` | No currently released patient projection exists | Generic patient-safe unavailable copy; never imply no care activity | +| `not_authorized` | Principal/relationship/grant does not permit disclosure | Anti-oracle response; no existence confirmation | +| `temporarily_unavailable` | Required source, audit, policy, or service is unavailable | Do not convert to “no stay” or empty clinical content | +| `contract_incompatible` | Client cannot safely understand the response/vocabulary | Withhold content, show safe update/support guidance, emit PHI-free compatibility telemetry | +| `account_review` | Identity/source/cardinality state is inconsistent | No handles or content; approved support path | +| `malformed` | Response violates exact schema, identifiers, bounds, timestamps, or registry | Reject the complete affected projection; invent no fields or identifiers | + +The public anti-oracle status may intentionally collapse multiple internal dispositions, but +the client still needs one approved, non-inferential presentation contract. A nullable model +alone is insufficient. + +### 8.3 Failure containment + +The future first-read coordinator must define whether projection reads are independently +contained or whether selected failures invalidate the whole screen. At minimum: + +- identity/context failure invalidates all downstream data; +- contract or identifier failure invalidates the affected projection and may require a + protective full-state transition; +- authorization is never cached from raw client scopes; +- stale data is never labeled current; +- one projection service failure does not silently erase unrelated released information; +- partial success cannot mix different principals, contexts, policy versions, or + authorization evaluations; +- refresh/retry is bounded and idempotent; +- backgrounding clears volatile identity and patient-authored input; +- all retained data is bound to principal, session, context, contract, and release version; + and +- sign-out, recovery, identity change, grant change, source inconsistency, or realm mismatch + clears protected state before another read. + +## 9. Detailed decision: error and non-disclosure + +### 9.1 Required error architecture + +Nightingale errors must be separated into four layers: + +1. **Internal diagnostic cause:** server logs and protected audit only; no raw patient, + source, credential, route, query, exception, or stack material in the public response. +2. **Stable contract code:** product-owned, operation-appropriate, versioned, and + anti-enumerating. +3. **Client disposition:** a closed enum that drives state transitions and retry/clear + behavior; unknown values fail closed. +4. **Patient copy:** locally owned, reviewed, localized, accessible, and never taken from an + arbitrary server message. + +The client must not display `message` from a decoded server failure. The server may still +send approved copy for non-native consumers, but native apps map the stable code to their own +versioned local catalog and record a PHI-free compatibility event when the code is unknown. + +### 9.2 Required equivalence sets + +At the public boundary, the following groups require indistinguishable status, body shape, +header behavior, material timing controls, and absence of identifying audit content: + +- unknown identity and known identity with wrong credential; +- unknown challenge, wrong secret, wrong code, expired, consumed, revoked, locked, wrong + principal, wrong identity link, and wrong grant; +- unknown context, cross-principal, wrong relationship, revoked/expired/suspended grant, + missing scope, unreleased, retracted, and policy-ineligible projection where existence must + not be disclosed; +- unknown session and cross-principal session; +- unknown/closed/revoked resources in any future mutation; and +- unsupported legacy path and disabled Nightingale operation. + +Internal audit can retain a bounded reason code where policy permits, but public behavior +must not become an oracle through code, copy, fields, headers, response length, ordering, +cache behavior, retry timing, or redirect. + +### 9.3 Required client clearing behavior + +| Trigger | Volatile identity input | Credentials/binding | Patient projection cache | Patient-authored draft | +| -------------------------------------- | --------------------------------------------------- | ----------------------------------------------------- | -------------------------------------- | ---------------------- | +| App background/inactive | Clear | Retain only approved device-bound descriptor | Cover; retention per approved policy | Clear | +| Authentication failure | Clear secrets; retain only safe locator if approved | No new binding | Clear mismatched state | Clear | +| Enrollment completion/failure | Clear all proof material | Persist only approved binding after full success | Load only after identity/context proof | Clear | +| Recovery requested/completed/failed | Clear | Invalidate according to approved recovery transaction | Clear before new binding | Clear | +| Principal/relationship/context changes | Clear | Re-evaluate/clear | Clear synchronously | Clear | +| Session revoked/expired/reuse detected | Clear | Clear | Clear synchronously | Clear | +| Sign-out | Clear | Clear | Clear | Clear | +| Contract/vocabulary incompatibility | Clear active form secrets | Retain only if identity remains valid | Withhold incompatible content | Clear if schema-bound | + +## 10. Principles eligible for future reimplementation + +After the relevant approvals, these bounded principles can be reimplemented under +Nightingale ownership: + +- product and operation default-off gates; +- independent identity realm and exact session ownership; +- comparable work for unknown and known credential failures; +- one-way storage of short-lived proof material; +- bounded attempts, expiry, consumption, revocation, and replay protection; +- row locking and atomic enrollment/recovery/session transitions; +- session rotation, reuse detection, revocation, and local-store failure cleanup; +- encrypted source linkage plus purpose-separated keyed lookup digests; +- self-only initial relationship; +- authoritative source states that distinguish unavailable, inconsistent, closed, and + current; +- zero/one initial context cardinality; +- request-time authorization and serialization-boundary recheck; +- random product-owned opaque handles; +- allowlist-first patient projection schemas; +- forbidden raw source and staff-only fields; +- immutable release, provenance, uncertainty, correction, and audit concepts; +- audit-before-disclosure; +- generic anti-oracle public responses; +- centralized safe-error mapping; +- exact no-store/no-index headers; +- strict identifier, enum, timestamp, size, and collection validation; +- no redirects, embedded credentials, disk HTTP cache, or unapproved production host; +- device-only non-synchronizing protected storage for an approved token-agnostic binding; +- secret zeroization where platform/runtime permits; and +- negative-test mutation patterns across backend, iOS, and Android. + +Every item still needs a Nightingale-owned implementation, contract, tests, and approvals. + +## 11. Behaviors explicitly rejected + +The following must not appear in Nightingale: + +- alias, proxy, redirect, or generated client for `/api/patient/v1`; +- reuse of the Hummingbird patient realm, models, provider, credentials, tokens, sessions, + storage keys, device UUID, grants, encounter UUIDs, routes, scopes, flags, policy versions, + or source keys; +- automatic migration of a legacy patient credential or protected-store record; +- email/password as an assumed identity default; +- a seven-field invitation form copied from either native client; +- treating an `account_recovery` enum as a recovery feature; +- client authorization from raw scopes; +- selecting the first encounter or projection because of server/database ordering; +- inventing UUIDs when the server sends malformed identifiers; +- mapping missing scope, forbidden, not found, unreleased, stale, incompatible, unavailable, + and genuine empty to one unqualified `nil`; +- presenting raw server or framework error messages; +- allowing environment variables to approve identity providers, patient sources, state + vocabulary, content, operations, or relationships; +- using grant-row update time as proof of current inpatient state; +- treating a maximum row version as a collection revision; +- exposing raw grant fields to make client navigation decisions; +- querying production to discover or validate a design before source/identity authority; +- replaying candidate fixtures in any environment; or +- creating a sample production patient under this milestone. + +## 12. Mechanical enforcement + +The new verifier: + +- parses the classification as strict JSON; +- requires the exact classification identifier, date, scope, and unapproved status; +- requires all fourteen global permission flags to remain `false`; +- requires exactly 65 unique safe repository-relative paths; +- pins the sorted source inventory by SHA-256; +- verifies every classified file exists and its content SHA-256 matches; +- requires exact fields for every source row; +- requires known surfaces, categories, and dispositions; +- requires a substantive file-specific decision; +- pins category counts so a domain cannot be silently emptied; +- requires every surface and disposition to remain represented; +- confirms the Nightingale configuration still has no route, network client, provider, + production query, disclosure, mutation, or production activation; +- confirms the contract foundation remains dormant; and +- rejects credential-like literals in the classification manifest. + +Its mutation self-test proves rejection of: + +- implementation activation; +- runtime adoption; +- first-record selection; +- server-message passthrough; +- projection-absence conflation; +- production replay; +- malformed source hashes; +- removed sources; +- duplicate/replaced source paths; and +- an `approved` source disposition. + +The verifier runs in the Nightingale CI job beside the empty-contract, encounter-candidate, +identity/source-candidate, backend-foundation, and product-boundary checks. + +## 13. Remaining gates + +Completion of this classification does not close the following: + +- [ ] Named identity-provider and assurance decision +- [ ] Enrollment proofing and delivery decision +- [ ] Full account-recovery decision and support operating model +- [ ] Representative relationship, consent, revocation, and sensitive-service decision +- [ ] Nightingale-owned profile field and preference ownership +- [ ] Nightingale session, credential, and device-binding design +- [ ] Authoritative inpatient source adapter approval +- [ ] Independent API operation approval +- [ ] Exact first-read response and error contract +- [ ] Closed client disposition model and localized copy catalog +- [ ] Projection content, vocabulary, provenance, uncertainty, freshness, and release approval +- [ ] Backend/iOS/Android canonical fixture parity +- [ ] Threat model, privacy/security, clinical safety, legal/HIM, accessibility/language, + patient-advisor, support, operations, and release approval +- [ ] Non-production integration with synthetic identities and source data +- [ ] Production-readiness evidence, exact-SHA CI, and protected-main release + +The next implementation slice should begin only after named independent identity, +proofing/recovery, source, contract, privacy/security, and patient-language decisions exist. +Until then, the correct runtime state remains the existing no-route, no-provider, +no-network-client, no-production-query Nightingale foundation. diff --git a/docs/nightingale/IDENTITY-RECOVERY-AND-PROTECTED-STATE-DECISIONS-2026-07-26.md b/docs/nightingale/IDENTITY-RECOVERY-AND-PROTECTED-STATE-DECISIONS-2026-07-26.md new file mode 100644 index 00000000..0507d85e --- /dev/null +++ b/docs/nightingale/IDENTITY-RECOVERY-AND-PROTECTED-STATE-DECISIONS-2026-07-26.md @@ -0,0 +1,227 @@ +# Nightingale identity, recovery, and protected-state decisions + +**Status:** Engineering foundation decision; implementation may protect synthetic +verification canaries only. This document does not approve an identity provider, patient +authentication, enrollment, recovery workflow, credential format, production patient, +representative access, API client, pilot, or release. + +**Decision date:** 2026-07-26 + +**Applies to:** `nightingale/iosApp` and `nightingale/androidApp` + +**Reference implementations:** `hummingbird/iosPatientApp` and +`hummingbird/androidPatientApp` are evidence inputs only. Their access-token, refresh-token, +device-identity, and storage choices are not inherited. + +**Companion design evidence:** The +[identity/session/recovery and inpatient-source held candidate](./IDENTITY-SESSION-RECOVERY-AND-SOURCE-CANDIDATE-DECISION-2026-07-26.md) +now defines exhaustive synthetic failure states and adoption gates. It does not satisfy the +provider, credential, representative, source, review, integration, pilot, or release +decisions that remain open below. + +## 1. Why this decision precedes authentication code + +A patient-facing app can create durable disclosure and account-takeover risk before it +shows clinical content. Token persistence, device migration, logout, recovery, retained +drafts, and deletion failure determine whether a former user, a restored device, an +interrupted workflow, or a compromised process can regain access. Nightingale therefore +defines the lifecycle and failure semantics before selecting an identity provider or +adding an authentication screen. + +The current native foundation remains no-network and no-data. The protected-state +implementation introduced under this decision is deliberately dormant in production code; +platform tests exercise it with conspicuously synthetic canary bytes and delete them in +teardown. + +## 2. Authoritative security inputs + +- Apple recommends selecting the most restrictive Keychain accessibility compatible with + the app's use and documents that + `kSecAttrAccessibleWhenUnlockedThisDeviceOnly` is available only while unlocked and does + not migrate to another device: + [Keychain accessibility](https://developer.apple.com/documentation/security/ksecattraccessiblewhenunlockedthisdeviceonly). +- Android documents that Android Keystore keys can remain non-exportable and can be + restricted to specified algorithms, purposes, modes, and padding: + [Android Keystore](https://developer.android.com/privacy-and-security/keystore). +- OWASP MASVS requires secure handling of intentionally stored sensitive data and + recommends minimizing durable sensitive state: + [MASVS-STORAGE-1](https://mas.owasp.org/MASVS/controls/MASVS-STORAGE-1/) and + [iOS data storage](https://mas.owasp.org/MASTG/0x06d-Testing-Data-Storage/). + +These sources establish implementation baselines, not Nightingale's complete threat model +or release approval. + +## 3. Protected-state inventory and decisions + +| State candidate | Foundation decision | Durable location | Backup/migration | Activation gate | +| -------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------- | -------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------ | +| Access token | **Do not persist.** If a future contract issues one, hold it in process memory only and make it short lived. | None | None | Approved Nightingale contract, identity threat model, expiry/rotation tests, and logout/revocation semantics | +| Refresh token | **No format or persistence approved.** The legacy clients' token keys are rejected. | None | None | Identity-provider selection plus security/privacy review | +| Future session binding | Reserve exactly one Nightingale-only protected value descriptor so platform protection and deletion can be tested without defining a credential. | iOS Keychain or Android Keystore-encrypted app-private ciphertext | Device only; Android application backup remains disabled | May store synthetic canaries now; real use requires identity approval | +| Device identifier | **Do not create a durable app UUID.** Installation, device, patient, encounter, and session identity must not be conflated. | None | None | Separate necessity, privacy, rotation, and support decision | +| Patient projection/cache | **Do not persist in this slice.** | None | None | Data-minimization, freshness, offline, correction, retraction, and deletion design | +| Message/question/teach-back draft | Volatile composition memory only. Clear when the app loses active foreground state and at logout, account switch, recovery, revocation, or deletion boundaries. | None | None | Patient-advisor review is required before changing the safe-first loss-on-background behavior | +| Analytics/crash context | No patient, credential, free text, source payload, or stable device identifier. No SDK is present. | None | None | Separate telemetry privacy decision and project isolation | +| Logs, screenshots, clipboard, keyboard cache | No sensitive values; use generic errors. Android keeps `FLAG_SECURE`; iOS uses the lifecycle cover. No clipboard behavior is added. | None | None | Platform-specific leakage test suite before patient inputs | + +### 3.1 Namespace + +The protected-state namespace is a product boundary: + +- iOS Keychain service: `net.acumenus.nightingale.protected-state.v1` +- iOS account descriptor: `future-session-binding-v1` +- Android Keystore alias: `net.acumenus.nightingale.protected-state-key.v1` +- Android private preference file: + `net.acumenus.nightingale.protected-state-ciphertext.v1` +- Android ciphertext record: `future-session-binding-v1` + +No Hummingbird service, package, alias, preference file, access-token key, refresh-token +key, device UUID, or migration rule is accepted. There is no Keychain access group, Android +shared user, app group, cloud synchronization, or cross-product credential sharing. + +Changing any identifier above is a versioned security migration, not a string cleanup. No +migration is defined by this decision; an old or unknown namespace remains unread. + +## 4. Platform protection profile + +### iOS + +The foundation store uses generic-password Keychain items with: + +- `kSecAttrAccessibleWhenUnlockedThisDeviceOnly`; +- `kSecAttrSynchronizable = false`; +- the data-protection Keychain; +- the exact Nightingale service and account descriptor; +- explicit status handling for read, write, and deletion. + +Foreground-only availability matches the current product boundary. A later requirement for +background refresh must not silently weaken the accessibility class. Passcode-set-only or +user-presence access remains an open usability/security decision for patient and +representative workflows; this foundation does not claim biometric protection. + +### Android + +The foundation store uses: + +- a non-exportable 256-bit AES key generated in `AndroidKeyStore`; +- encrypt/decrypt purposes only; +- AES/GCM/NoPadding with a fresh platform-generated IV for every write; +- authenticated additional data binding ciphertext to the Nightingale application, + schema, and descriptor; +- app-private `SharedPreferences` for versioned IV and ciphertext only; +- `android:allowBackup="false"` plus explicit all-domain cloud-backup, device-transfer, and + legacy full-backup exclusion rules. + +Hardware-backed key storage is device-dependent and is not claimed by this foundation. +StrongBox and per-use user authentication are not forced because support, accessibility, +representative access, and recovery requirements are undecided. This must be revisited +before any real binding is stored. + +The legacy `EncryptedSharedPreferences` implementation and its persistent access token, +refresh token, session UUID, and device UUID are not migrated. + +## 5. Read, write, and corruption semantics + +1. Missing protected state is an unauthenticated state, never an implicit enrollment or + recovery. +2. A platform access, Keychain, Keystore, decode, authentication-tag, or persistence error + is surfaced as protected storage unavailable. The app must withhold access; it must not + fall back to plaintext, another namespace, a legacy key, or a cached patient identity. +3. Writes must not log the value or include it in an error. +4. Empty values are rejected. +5. Android ciphertext is one versioned binary envelope. An unknown version, invalid IV, + absent key with surviving ciphertext, or failed GCM authentication is unavailable state, + not "no account." +6. A future caller must not infer remote session validity from successful local decryption. + +## 6. Logout, revocation, deletion, and recovery state machine + +Local protected-state deletion and server-side session revocation are separate facts: + +| Event | Required local action | Required remote action | Patient-visible claim | +| ------------------------------------------ | -------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------- | +| User logout while online | Immediately clear volatile inputs and make protected state cryptographically inaccessible | Revoke the exact server session idempotently | "Signed out" only after local access is removed; separately state if server confirmation is pending | +| User logout with ambiguous network outcome | Same local action | Retry only an idempotent revocation operation; never replay another write | Do not claim remote revocation succeeded | +| Server revocation/expiry | Clear volatile and protected state as soon as the response is authenticated | Server remains authoritative | Generic signed-out/re-authentication state with no existence disclosure | +| Account switch or representative change | Clear volatile state before transition; never reuse another principal's protected state | End or replace the scoped session under an approved contract | Never display the former principal while resolving the new one | +| Local "remove from this device" | Clear volatile state and protected state | No account deletion claim | State only what was removed locally | +| Account/record deletion request | Clear local state after the governed request reaches its defined state | Separate HIM/legal/retention workflow | Never imply that legally retained records or audit events were erased | +| Recovery | Start from no trusted local identity; clear old local binding before accepting a replacement | Perform independently proofed, rate-limited recovery and revoke superseded sessions | Never reveal whether an identifier belongs to a patient before proofing | + +Deletion is idempotent. "Already absent" is success. Any platform error is failure and must +be observable without exposing a secret. On Android, deletion attempts both the Keystore +key and ciphertext. Removing the cryptographic key makes surviving ciphertext +unrecoverable, but cleanup failure must still be reported rather than silently called +complete. + +## 7. Volatile input lifecycle + +The initial policy intentionally favors disclosure prevention over draft retention: + +```text +foreground composition + | + +-- app becomes inactive/backgrounded --> release in-memory draft reference + +-- logout/revocation -----------------> release in-memory draft reference + +-- account/representative transition --> release in-memory draft reference + +-- recovery/deletion -----------------> release in-memory draft reference +``` + +The implementation can clear references but cannot prove that immutable language/runtime +strings have been immediately overwritten in process memory. It therefore makes no +zeroization claim. Future patient input controls must additionally address keyboard +learning, dictation, autofill, screenshots, accessibility announcements, logs, crash +reports, and process restoration. Draft persistence is prohibited until a separately +reviewed encrypted-offline and patient-experience design exists. + +## 8. Device compromise posture + +Root, jailbreak, debugger, hooking, sideload, integrity, and attestation signals can be +useful risk inputs but are not complete or perfectly reliable. Nightingale will not: + +- claim that a device is safe merely because no compromise signal was detected; +- use a client-only signal as the sole authorization control; +- send patient details in a compromise error; +- fall back to weaker storage when hardware or policy is unavailable; or +- strand a patient without a documented bedside/support/recovery alternative. + +Before pilot, the security review must decide which signals are collected, their privacy +and accessibility effects, server policy, false-positive handling, representative-device +handling, emergency support language, and whether the app withholds all content or only +sensitive capabilities. The current foundation only fails closed on protected-storage +errors. + +## 9. Verification and evidence requirements + +The foundation slice is complete only when all of the following are true: + +- unit tests pin independent Nightingale namespaces and prove no access/refresh-token or + durable-device-UUID keys exist; +- iOS Simulator tests round-trip and idempotently delete a synthetic Keychain canary; +- Android emulator tests round-trip a synthetic canary, prove the private preference record + does not contain the plaintext canary, and delete both ciphertext and Keystore key; +- both platforms prove volatile drafts are cleared at their lifecycle boundary; +- Debug and Release builds pass and the existing no-network/product-boundary scans remain + green; +- tests clean up canaries even after assertion failure; +- no production patient, credential, API, database, grant, session, or deployment is used. + +The tests prove platform plumbing and defined failure behavior only. They do not prove +resistance to a compromised OS, identity correctness, secure server revocation, recovery +adequacy, clinical safety, privacy approval, or pilot readiness. + +## 10. Required decisions before live identity work + +- [ ] Select and contractually document the patient/representative identity provider. +- [ ] Define proofing assurance, enrollment authority, activation delivery, rate limits, + lockout, non-disclosing errors, and help-desk verification. +- [ ] Define patient, guardian, proxy, interpreter, and representative relationship changes. +- [ ] Define session, rotation, expiry, device-limit, revocation, and risk-event contracts. +- [ ] Decide whether a refresh credential exists and, if so, its binding, accessibility, + user-presence, recovery, and deletion requirements. +- [ ] Define remote logout ambiguity and idempotency behavior. +- [ ] Approve recovery and lost/stolen/shared-device procedures through security, privacy, + legal/HIM, accessibility, patient-advisor, support, and clinical review. +- [ ] Complete mobile threat modeling, penetration testing, and device-compromise policy. +- [ ] Add a Nightingale-owned default-off API contract and authorization matrix. +- [ ] Obtain pilot and release approval separately. diff --git a/docs/nightingale/IDENTITY-SESSION-RECOVERY-AND-SOURCE-CANDIDATE-DECISION-2026-07-26.md b/docs/nightingale/IDENTITY-SESSION-RECOVERY-AND-SOURCE-CANDIDATE-DECISION-2026-07-26.md new file mode 100644 index 00000000..d23c47dd --- /dev/null +++ b/docs/nightingale/IDENTITY-SESSION-RECOVERY-AND-SOURCE-CANDIDATE-DECISION-2026-07-26.md @@ -0,0 +1,485 @@ +# Nightingale identity, session, recovery, and inpatient-source held-candidate decision + +**Decision date:** 2026-07-26 + +**Status:** Adopted as non-runnable engineering decision evidence only. No identity +provider, proofing method, credential, enrollment channel, session representation, recovery +channel, representative authority, source adapter, source query, facility cohort, freshness +threshold, API operation, client, patient access, non-production integration, pilot, or +production use is approved. + +**Runtime state:** Code-owned disabled configuration; unconfigured identity and source +ports; zero Nightingale OpenAPI paths; zero Nightingale routes; zero native network clients. + +**Related records:** + +- [Product implementation plan](../plans/nightingale-patient-product-2026-07-26.md) +- [Identity, recovery, and protected-state foundation](./IDENTITY-RECOVERY-AND-PROTECTED-STATE-DECISIONS-2026-07-26.md) +- [Route, compatibility, identity, and source ADR](./ROUTE-COMPATIBILITY-IDENTITY-SOURCE-ADR-2026-07-26.md) +- [Encounter-access held candidate](./ENCOUNTER-ACCESS-CANDIDATE-DECISION-2026-07-26.md) +- [Contract ownership and authorization matrix](./CONTRACT-OWNERSHIP-AND-AUTHORIZATION-MATRIX-2026-07-26.md) +- [Identity candidate](./identity/candidates/v0/candidate.json) and + [64 synthetic identity fixtures](./identity/candidates/v0/fixtures.json) +- [Current-inpatient source candidate](./source-candidates/current-inpatient/v0/candidate.json) + and + [42 synthetic source fixtures](./source-candidates/current-inpatient/v0/fixtures.json) + +## 1. Decision summary + +This slice resolves what can be resolved safely from current source evidence and leaves +policy choices that require accountable human authority explicitly unresolved. + +| Boundary | Adopted now | Explicitly not adopted | +| ---------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------- | +| Identity | Three request-scoped evaluation states: `unavailable`, `denied`, and `verified_self`; only `verified_self` can reach later governed evaluation | Identity provider, issuer, protocol, login screen, password, passkey, federated claim, credential, staff identity, or legacy patient identity | +| Audience | Initial positive-state reasoning is limited to a verified self relationship | Representative, guardian, caregiver, proxy, minor, sensitive-service, or break-glass authority | +| Session | Fail-closed predicates for missing, expired, revoked, mismatched, reused, risky, unknown, or concurrently changed sessions | Access/refresh token formats, cookies, device binding, durations, limits, rotation protocol, or service implementation | +| Enrollment | Evidence categories and failure behavior only | Invitation authority, channel, code format, proofing level, delivery, rate limit, activation, or support process | +| Recovery | Seven safety invariants and five synthetic recovery cases | Recovery channel, support proof, provider API, replacement credential, patient-facing flow, or activation | +| Protected state | Existing prohibition on persisted access credentials and durable device identifiers; dormant future-binding descriptor remains test-only | Real secret persistence, cross-product migration, cloud synchronization, or production caller | +| Inpatient source | Four request-scoped states: `unavailable`, `inconsistent`, `confirmed_closed`, and `confirmed_current`; only `confirmed_current` can reach later governed evaluation | Source adapter, database connection, query, raw status mapping, facility/unit/patient-class cohort, threshold, or source identifier | +| Authorization | Identity and source are independent prerequisites that can only continue to later evaluation when both are positive | Granting encounter access, issuing a handle, returning patient data, or activating the held operation | + +The design is intentionally stricter than the legacy reference. A positive identity result +does not establish current inpatient status. A positive source result does not establish +identity. Their conjunction still does not establish relationship, grant, purpose, +cardinality, handle ownership, audit completion, race stability, disclosure policy, or +serialization authority. + +## 2. Evidence baseline and reproducibility + +The review used repository source from branch `codex/nightingale-patient-product`, based on +`origin/main` commit `84b5f830`. Hashes make the observations reproducible and prevent a +later file with the same name from being treated as identical evidence. + +### 2.1 Legacy identity, enrollment, and session evidence + +| Source | SHA-256 | Material observation | +| ----------------------------------------------------------------------------------------- | ------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `app/Services/Patient/PatientAuthService.php` | `98fcfd11460870f75eb5ea7cbb6e5cbf4f45fefe2fccc0c1b5ad50f0a0bf214d` | Local email/password exchange, enrollment challenge completion, access/refresh issuance, rotation, family reuse response, and revocation are coupled in one legacy service. | +| `app/Http/Middleware/EnsurePatientRealm.php` | `0f4c513a0aeb39e8509417e7d3ed885c5a0ed6ba5d31072bd278a411af1dcc67` | Legacy request identity is coupled to Hummingbird token names, abilities, and patient-session state. | +| `app/Http/Controllers/Api/Patient/AuthController.php` | `2e53bd85515c7eabf476724574a0035f5c77236dcdae8fb5d57e61ea016eb6c7` | Enrollment, credential exchange, refresh, and revoke are exposed as legacy patient operations. | +| `app/Http/Requests/Patient/VerifyEnrollmentChallengeRequest.php` | `db606dff38332eda36cef5b060479771f331f1d71a3eac83499cf5dde47c5805` | The request is a two-part challenge/code design, not a provider-neutral Nightingale proofing contract. | +| `app/Http/Requests/Patient/TokenRequest.php` | `f1030e037737514987c0f1bd9921786652c080bf4a856fc7fd54fc1625680aac` | The legacy credential exchange accepts email/password and device metadata. | +| `app/Models/Patient/PatientPrincipal.php` | `25fd0415462d4ad0e63020b40678e79328cebd92e8b9ba9d6dbf9c5052218370` | The existing model carries patient/representative vocabulary but is not proof of an approved Nightingale realm. | +| `app/Models/Patient/PatientIdentityLink.php` | `14c975603f2417769e45ccef9d7c82dd1ef9bf89955181522127d09e36645dea` | An identity-link record exists, but current verification, ownership, merge, ambiguity, and assurance must be independently evaluated. | +| `app/Models/Patient/PatientEnrollmentChallenge.php` | `85fe910b3e4b457013773b80063416798da93208322de67659b939f61d9c7e53` | Challenge storage supports multiple purpose labels; labels do not provide runnable recovery or representative workflows. | +| `app/Models/Patient/PatientSession.php` | `b18a83a4bdc92591a2bc4a220d633d84ff5c6febeb853314d0e11299ea5195d6` | Persistent session families and device metadata are legacy choices, not Nightingale requirements. | +| `database/migrations/2026_07_19_000100_create_patient_experience_identity_foundation.php` | `f093d43a7a84b00ee519f6b1392a8aee5018ab1f43d930ea5abf1c4d7ecaa1e2` | The schema admits patient/representative principals, six relationship values, and recovery/invitation challenge purposes, but schema vocabulary is not operational or legal approval. | +| `tests/Feature/Patient/PatientAuthLifecycleTest.php` | `3603200dbd738e14a26679e761950f8f310c28cd32b6ebc30f5b96f7d6860d1d` | The reference tests prove selected legacy enrollment/token/refresh behavior, not provider-neutral Nightingale assurance or recovery. | +| `tests/Feature/Patient/PatientSessionManagementTest.php` | `8ec0665944bc49c451af6ac40a50ab96e9ccefbbdea51f61a48a960286ad0bce` | The reference tests prove principal-scoped list/revoke behavior and indistinguishable unknown/cross-principal handling, but do not decide Nightingale session policy. | + +### 2.2 Current-inpatient source evidence + +| Source | SHA-256 | Material observation | +| --------------------------------------------------------------------------------------- | ------------------------------------------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------- | +| `app/Models/Encounter.php` | `59e879ebe2780d093cb73328df30ad437519742aedabd0c02cb3a2cf22caa516` | The `active` scope checks status and deletion, but not the complete current-inpatient lifecycle required here. | +| `app/Services/Patient/PatientEncounterAccessService.php` | `a593503e20dc8ba45699af4974f36345a0e85b739fa2f428565c8d3d4b079ef9` | The legacy list begins from effective grants and does not independently establish source completeness or canonical current state. | +| `app/Services/Patient/Messaging/PatientCommunicationEncounterGuard.php` | `8a65438b3779252e88c35ee1dd91cc40e0d0d74e8018f5ac1a1d7a210a617976` | Messaging adds a null-discharge requirement and locked grant/source linkage, showing that `status = active` alone is insufficient. | +| `app/Services/Patient/Messaging/PatientCommunicationLifecycleReconciliationService.php` | `2676d63146f7c88ef8a53fc75b4f26abc01a879795edd328e3b3544034787347` | Reconciliation distinguishes canonical active, canonical discharged, and unresolved combinations, including race-sensitive mutation behavior. | +| `docs/hummingbird/api-contract/hummingbird-patient.v1.yaml` | `fb6220b4ef8eb106223624a9785256fdc1603995f281f430a79897905cb45a1b` | The legacy transport is compatibility evidence and cannot define the Nightingale source adapter or identifier policy. | + +This review was source-only. It did not connect to, query, sample, profile, modify, or replay +production data. Production evidence would not resolve missing product, identity, legal, +clinical-operations, source-governance, or release authority and therefore is not a +permitted shortcut. + +## 3. Legacy behavior that is not inherited + +### 3.1 Local credentials and token families + +The reference flow binds a local email/password principal to persistent access and refresh +credential families and device descriptors. It transactionally activates enrollment, +creates grants, and issues a session. Refresh preserves predecessor evidence to detect +reuse and revoke a family. Those controls are valuable threat inputs, but the following +choices remain rejected for Nightingale: + +- email as a canonical patient identifier; +- a local password database as the selected authority; +- Hummingbird token names or abilities; +- persistent access credentials; +- an assumed refresh credential; +- a durable app-generated device UUID; +- device labels as identity proof; +- automatic import of legacy principals, sessions, links, grants, or credentials; and +- shared staff, legacy patient, or Nightingale authentication realms. + +### 3.2 Enrollment, recovery, and representatives + +The reference schema includes `account_recovery`, `identity_link`, and +`representative_invitation` challenge purposes. It includes `patient` and `representative` +principal types and `self`, `legal_representative`, `guardian`, `caregiver`, `proxy`, and +`other` relationships. Repository inspection did not find a complete runnable recovery or +representative invitation/acceptance/review/revocation route and service lifecycle. + +Therefore: + +- a schema enum is not an implemented journey; +- an implemented journey would still not establish legal or privacy approval; +- a representative principal is categorically held in the initial candidate; +- every non-self relationship is denied at the identity boundary, without disclosing + whether the patient, account, invitation, or relationship exists; and +- no patient field or communication ability is inherited by a representative by default. + +### 3.3 Operational encounter state + +The reviewed source uses multiple related definitions of active/current. The database +schema comment describes `active | discharged`, but the schema does not constrain the +status to those values. Status, deletion, admission/discharge timestamps, linkage, +transfer, correction, merge, and concurrent-change facts can contradict one another. + +Nightingale therefore does not equate: + +- a row existing with an inpatient context; +- no row with a confirmed closed encounter; +- `status = active` with a complete current determination; +- a valid grant with a current encounter; +- a null discharge timestamp with a valid admission; +- a source record with a patient-visible identifier; or +- successful source evaluation with authorization. + +## 4. Identity candidate semantics + +The machine-readable candidate defines exactly three states: + +| State | Required interpretation | Continue to later governed evaluation? | Authorizes patient access? | +| --------------- | ---------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------: | -------------------------: | +| `unavailable` | Required identity authority, policy, assurance, principal, session, link, audit, clock, or stable evidence is unavailable or internally unsafe | No | No | +| `denied` | Evaluation completed sufficiently to reject the presented or permitted identity state without disclosing account existence | No | No | +| `verified_self` | Every candidate self-identity prerequisite is satisfied at the evaluation instant | Yes | No | + +`verified_self` is deliberately named more narrowly than `authenticated`. It says nothing +about an encounter, grant, purpose, source, resource, field, or communication capability. +It cannot be cached as durable authorization or passed from the staff or legacy patient +realm. + +### 4.1 Positive-state prerequisites + +All of the following must be true under one approved and versioned policy before a future +adapter can return `verified_self`: + +1. product and identity policy gates are enabled for the exact environment and cohort; +2. the selected provider/authority has completed an approved evaluation; +3. issuer, audience, client, nonce, signature, expiry, and replay controls are valid; +4. required assurance and any step-up are satisfied; +5. the principal is active, self-relationship, and in the independent Nightingale realm; +6. the request-scoped session is active, unexpired, unreused, acceptable under risk policy, + owned by that principal, and stable through handoff; +7. exactly one verified/current/unmerged/unrevoked identity link belongs to that principal; +8. no recovery or representative transition is in progress; +9. a durable, redacted identity-evaluation audit is committed; and +10. all later authorization gates remain mandatory. + +The candidate does not supply the provider, policy, claims, durations, database model, +middleware, or adapter needed to satisfy those predicates. + +### 4.2 Identity fixture coverage + +The 64 synthetic cases are exhaustive for this candidate version. Their category counts +are: + +| Category | Cases | Safety purpose | +| -------------- | ----: | ---------------------------------------------------------------------------------- | +| Activation | 2 | Product or identity capability disabled | +| Dependency | 4 | Provider unconfigured, unavailable, timed out, or malformed | +| Evidence | 8 | Missing, invalid, expired, issuer/audience/client/nonce mismatch, or replay | +| Assurance | 3 | Missing/below-policy assurance or incomplete step-up | +| Principal | 8 | Missing, wrong realm, staff, pending, inactive, locked, suspended, or closed | +| Session | 6 | Missing, expired, idle-expired, revoked, wrong principal, or wrong realm | +| Integrity | 6 | Reuse, risk, unknown state, inconsistent principal, or unresolved limits | +| Identity link | 9 | Missing, pending, ambiguous, merged, revoked, unverified, mismatched, or duplicate | +| Recovery | 5 | Requested, incomplete, uncleared binding, live superseded session, or fresh proof | +| Representative | 6 | Generic representative plus each currently held relationship class | +| Audit | 1 | Durable evaluation audit unavailable | +| Policy | 3 | Correlation, clock, or policy-version failure | +| Race | 2 | Session or identity changes during evaluation/handoff | +| Positive | 1 | Every self prerequisite satisfied | + +Every fixture includes an expected state and audit mode. Both `verified_self` fixtures still +set `authorizes_patient_access` to false. Negative mutation tests prove the verifier rejects +a provider selection, representative activation, access-granting positive state, weakened +cross-principal outcome, or production replay. + +## 5. Session decision boundary + +No Nightingale session format exists yet. The candidate establishes only fail-closed +properties that any proposed session must satisfy. + +| Concern | Required future proof | Current result | +| ---------------- | ----------------------------------------------------------------------------------------- | ----------------------------------------------------- | +| Ownership | Session belongs to the exact Nightingale principal and realm | Wrong principal is unavailable; wrong realm is denied | +| Lifetime | Absolute and idle expiry are explicit, versioned, and evaluated against a trusted clock | Missing/expired/idle-expired is denied | +| Revocation | Revocation is authoritative and takes effect before disclosure | Revoked is denied | +| Rotation/reuse | A credential family, if approved, detects replay and revokes/contains the affected family | Reuse is denied and durably audited | +| Risk | Risk hold and step-up policy are explicit and do not silently degrade | Risk hold is denied | +| Cardinality | Device/session limits have approved shared-device and accessibility behavior | Unresolved limit policy is unavailable | +| Race | Principal/session/link facts are stable through authorization handoff | Concurrent change is unavailable | +| Patient language | Errors do not disclose account, identifier, relationship, or encounter existence | Only bounded state is modeled; no copy is approved | + +The candidate does not decide whether a refresh credential exists. If one is later proposed, +its need, format, sender constraint or binding, rotation, user presence, device loss, +recovery, revocation, storage, expiry, and support behavior require separate approval. + +## 6. Recovery invariants + +A future recovery proposal must preserve every invariant below: + +1. begin with no trusted local identity; +2. clear the old local binding before accepting a replacement; +3. require independently verified fresh proof; +4. revoke superseded server sessions before returning a positive identity state; +5. never reveal whether an identifier belongs to a patient before proofing; +6. never call an ambiguous remote outcome successful; and +7. provide a reviewed patient-support path before activation. + +`recovery_fresh_proof_complete` is a design fixture, not an implemented recovery path. It +can return candidate state `verified_self` only when the full positive-state prerequisites +also hold. It still does not authorize access. No recovery email, SMS, voice call, bedside +code, help-desk workflow, or provider API has been selected. + +## 7. Representative boundary + +Initial candidate access is self-only. Legal representative, guardian, caregiver, proxy, +and other relationships remain held because they require at least: + +- authoritative relationship source and proof; +- patient consent or applicable legal authority; +- start, expiry, suspension, revocation, and re-verification; +- minor/dependent and age-of-majority transitions; +- confidential encounter and sensitive-service exclusions; +- field-level versus operation-level authority; +- whether the representative can send, amend, or close communications; +- audit actor/subject separation; +- notification routing and shared-device behavior; +- patient and representative visibility of relationship changes; +- non-disclosing invitation, acceptance, decline, and recovery; and +- clinical, privacy, security, legal/HIM, accessibility, patient-advisor, and support + approval. + +No representative is converted to self, and no self result can be reused to impersonate a +representative or patient. + +## 8. Current-inpatient source candidate semantics + +The source candidate defines exactly four states: + +| State | Required interpretation | Continue to later governed evaluation? | Authorizes patient access? | +| ------------------- | ---------------------------------------------------------------------------------------------------------------- | -------------------------------------: | -------------------------: | +| `unavailable` | Source, policy, scope, linkage, freshness, audit, clock, or completeness cannot support a reliable determination | No | No | +| `inconsistent` | Available facts conflict, cardinality is unsafe, or lifecycle/version/transition evidence cannot be reconciled | No | No | +| `confirmed_closed` | Exactly one coherent lifecycle is authoritatively confirmed closed under the approved policy | No | No | +| `confirmed_current` | Exactly one coherent current inpatient lifecycle is confirmed at the evaluation instant | Yes | No | + +`confirmed_closed` is intentionally distinct from unavailable and inconsistent. A future +patient journey may need a governed transition response after discharge, but the current +encounter-access candidate cannot continue on a closed state. + +### 8.1 Positive and closed prerequisites + +A future adapter must prove at least: + +- an approved authoritative source, query contract, precedence rule, and policy version; +- a trusted evaluation clock and approved freshness rule; +- a verified identity-to-source linkage without exposing source identifiers; +- explicit facility, unit, patient-class, and cohort inclusion; +- exactly one coherent lifecycle; +- complete admission, status, transfer, discharge, deletion, and version facts required by + policy; +- no unresolved merge, correction, retraction, transfer, or concurrent change; +- stable evidence through governed handoff; and +- durable source-evaluation audit. + +For `confirmed_closed`, discharge finality and absence of a conflicting active or pending +transfer record are also required. + +### 8.2 Freshness is intentionally unresolved + +The candidate does not invent a numeric maximum source age. Its policy version, evaluation +clock, and `maximum_source_age_seconds` remain null, and `thresholds_approved` remains false. +A threshold depends on the authoritative source's delivery guarantees, clinical-operational +meaning, unit/facility workflow, downtime behavior, safety analysis, and support plan. + +Until approved: + +- a missing observation time is unavailable; +- stale evidence is unavailable; +- an untrusted clock is unavailable; +- a missing policy is unavailable; and +- no default number may be introduced in config, fixtures, native code, or an adapter. + +### 8.3 Source fixture coverage + +The 42 synthetic cases are exhaustive for this candidate version: + +| Category | Cases | Safety purpose | +| ------------------------ | ----: | ----------------------------------------------------------------------- | +| Positive / closed | 2 | Exactly one fully proven current or closed lifecycle | +| Activation | 2 | Product or source capability disabled | +| Dependency | 4 | Adapter/source/database unavailable or timed out | +| Freshness | 4 | Policy, observation time, staleness, or clock failure | +| Scope | 4 | Facility, unit, patient class, or cohort policy not eligible/evaluable | +| Linkage | 4 | Missing, unverified, duplicate, or ambiguous patient linkage | +| Lifecycle | 10 | Missing/unknown/deleted/contradictory status and timestamps | +| Concurrency / transition | 5 | Missing version, source race, transfer, correction/merge, or retraction | +| Absence / completeness | 2 | No qualifying evidence or required field absent | +| Integrity / policy | 3 | Malformed source, missing policy, or policy-version mismatch | +| Cardinality | 1 | More than one current context | +| Audit | 1 | Durable source evaluation cannot be recorded | + +A source outage or missing record never becomes `confirmed_closed`. A stale record never +becomes `confirmed_current`. A contradictory lifecycle never becomes a false empty result. +Negative mutation tests prove the verifier rejects source-query activation, a stale/current +misclassification, a configured source adapter, or a runtime OpenAPI path. + +## 9. Combined precondition behavior + +The existing backend foundation enumerates all 12 identity/source combinations. + +```text +verified_self + confirmed_current + | + v +continue_to_governed_evaluation + | + +--> identity-link ownership + +--> relationship + +--> grant and purpose + +--> facility/cohort + +--> opaque handle + +--> zero/one cardinality + +--> race recheck + +--> request/disclosure audit + +--> response and field-release policy + +every other combination --> withhold +``` + +The continuation disposition is an internal prerequisite result only. It cannot be +serialized as a success, stored as an access grant, used as a native session, or interpreted +as approval to query or disclose data. + +## 10. Mechanical enforcement + +The new dependency-free verifier cross-checks: + +- the foundation OpenAPI artifact still has zero paths and every activation false; +- `config/nightingale.php` remains code-owned, provider/source-null, route-disabled, + query-disabled, disclosure-disabled, mutation-disabled, and production-disabled; +- candidate identities and held statuses; +- every runtime provider, credential, route, adapter, query, and database field remains + null or prohibited; +- every candidate activation gate remains false; +- PHP enum values exactly equal the machine-readable candidate state vocabularies; +- only `verified_self` and `confirmed_current` permit later governed evaluation; +- no state authorizes patient access; +- identity and source fixture sets contain exactly 64 and 42 unique required cases; +- every case has its pinned outcome and audit mode; +- fixtures are synthetic-only, contain no credentials/source identifiers, and prohibit + production replay; +- source freshness thresholds remain unselected; and +- network endpoints, credential assignments, email/IP literals, and raw patient/source + identifier field names do not enter the machine-readable candidates. + +Nine negative mutations verify rejection of: + +1. selecting an identity provider; +2. activating representatives; +3. making `verified_self` authorize access; +4. weakening a cross-principal session fixture; +5. permitting production fixture replay; +6. enabling a source query; +7. calling stale source evidence current; +8. adding a runtime OpenAPI path; and +9. configuring a legacy source adapter. + +CI runs this verifier with its negative self-tests alongside the foundation, encounter +candidate, backend truth-table, and native no-network/product-boundary verifiers. + +## 11. Approval gates before implementation + +Candidate design is complete for this version; adoption is not. Implementation remains held +until evidence names accountable owners and independently resolves: + +### Identity, enrollment, and session + +- provider/authority, protocol, issuer, audience, client, key lifecycle, and downtime; +- proofing level, evidence sources, accessibility alternatives, and fraud controls; +- invitation/enrollment authority, channel, expiry, rate limits, retries, and support; +- session representation, absolute/idle expiry, rotation, revocation, risk, and limits; +- access/refresh credential need, binding, storage, user presence, and deletion; +- generic error taxonomy and durable redacted audit events; and +- provider/data-processing/privacy/security/legal review. + +### Recovery and representatives + +- lost/stolen/shared/replaced-device behavior; +- independent proof and support-assisted escalation; +- ambiguous provider outcomes and idempotent retry; +- superseded session containment; +- relationship proof, scope, start/expiry/revocation, minors, sensitive services, and + confidential encounters; +- actor-versus-subject audit and notification rules; and +- patient/representative language, accessibility, and support validation. + +### Inpatient source + +- authoritative source and precedence; +- exact adapter and query contract; +- facility/unit/patient-class/cohort configuration; +- admission, observation, boarding, transfer, readmission, discharge, deletion, merge, + correction, and retraction semantics; +- freshness threshold, clock, time-zone, source-observed time, and downtime behavior; +- linkage, cardinality, completeness, version/race, and audit controls; +- opaque handle issuance and source-identifier containment; and +- approved synthetic/deidentified non-production integration and rollback. + +### Cross-cutting authorization and release + +- operation-specific non-disclosure and IDOR tests; +- request, denial, safety-failure, and disclosure audit durability; +- threat model, abuse cases, mobile security test, privacy review, and penetration testing; +- patient advisor, accessibility, language, clinical/content, legal/HIM, support, + operations, data-governance, and release approvals; +- default-off feature/cohort controls and production-like failover tests; and +- protected-main exact-SHA CI and manual release evidence. + +## 12. Implementation sequence after approval + +When the applicable gates are approved, implementation should proceed in independently +reviewable slices: + +1. add the approved policy schemas and exact synthetic fixtures without runtime bindings; +2. implement provider and source adapters behind the existing ports, default-off and + non-production-only; +3. add provider/source contract tests, nondisclosure tests, outage tests, clock/freshness + tests, race tests, audit tests, and representative/recovery tests; +4. add an operation to a new non-foundation Nightingale OpenAPI version only after the + route owner and authorization matrix are approved; +5. generate isolated Nightingale clients and retain native networking behind a default-off + compile/runtime boundary; +6. validate canonical fixtures across Laravel, iOS, and Android on signed simulator and + API 35 emulator targets; +7. perform approved deidentified non-production integration with rollback and support + exercises; +8. authorize a bounded pilot separately; and +9. merge and release only through protected `main`, exact-SHA green CI, deployment check, + and the canonical manual deploy path. + +No step may infer completion of the next step. A green fixture verifier is not provider +approval, a bound adapter is not route approval, a route is not patient disclosure +approval, and a successful deployment is not clinical or pilot authorization. + +## 13. Residual risks and current disposition + +| Risk | Current control | Residual state | +| ----------------------------------------------------- | --------------------------------------------------------------------------------------------- | -------------------------------------------------- | +| Legacy identity silently becomes Nightingale identity | Independent realm, provider null, legacy realm prohibited, verifier mutation | Held until provider/proofing approval | +| Representative gains patient-wide access | Self-only candidate, all representative fixtures denied | Held until relationship and field/action policy | +| Recovery trusts a lost or copied device | No durable access credential/device UUID; recovery begins with no trusted local identity | Held until independent proof and support design | +| Source outage appears as no encounter/discharge | Unavailable is distinct from confirmed closed; no source query exists | Held until adapter/downtime policy | +| Stale or contradictory encounter appears current | Freshness unresolved fails unavailable; contradictions fail inconsistent | Held until policy/adapter testing | +| Positive prerequisite is mistaken for authorization | All state artifacts set authorization false; 12-state gate continues only to later evaluation | Requires continued verifier and review enforcement | +| Source identifiers leak to clients/fixtures | Port returns state only; candidate forbids source identifiers; no client exists | Requires adapter and response review | +| Production data is used to accelerate design | Explicit prohibition; synthetic-only fixtures; production replay verifier | No production access in this slice | + +The correct current user-visible behavior remains the no-live-access foundation shell. diff --git a/docs/nightingale/JOURNEY-PREFERENCE-PRESENTATION-RELEASE-SOURCE-CLASSIFICATION-2026-07-26.md b/docs/nightingale/JOURNEY-PREFERENCE-PRESENTATION-RELEASE-SOURCE-CLASSIFICATION-2026-07-26.md new file mode 100644 index 00000000..0c7af6e6 --- /dev/null +++ b/docs/nightingale/JOURNEY-PREFERENCE-PRESENTATION-RELEASE-SOURCE-CLASSIFICATION-2026-07-26.md @@ -0,0 +1,825 @@ +# Nightingale journey, preference, presentation, synthetic, and release source classification + +**Decision date:** 2026-07-26 + +**Reviewed source commit:** `45ddf907c0f15e378b37a1b0726724e346cb29fd` + +**Last source revalidation:** 2026-07-27 + +**Status:** Complete source classification; evidence only; no implementation, route, +network client, source adapter, preference persistence, patient mutation, pathway release, +synthetic runtime, migration, production query, patient creation, deployment, or pilot +activation is approved. + +**Machine-readable ledger:** +[`migration/candidates/v0/journey-preference-presentation-release-source-classification.json`](./migration/candidates/v0/journey-preference-presentation-release-source-classification.json) + +**Verifier:** +`scripts/ci/verify-nightingale-journey-preference-release-classification.mjs` + +## 1. Decision + +The legacy Hummingbird Patient source-classification phase is now closed at repository +scope. The tracked product universe contains exactly **256 sources**: + +- **122 unique sources** were already covered by the identity/first-read and + communication/notification ledgers; +- this final slice classifies the remaining **134 sources**; and +- the union covers all **256 of 256** sources in the defined universe. + +This is classification completeness, not functional parity, implementation completeness, +clinical approval, release readiness, or permission to copy the legacy patient app. + +The final 134 sources resolve to exactly one required migration class each: + +| Required class | Sources | Result | +| ------------------------- | ------: | ----------------------------------------------------------------------------------------- | +| Reusable safety primitive | 42 | Retain the control objective; independently reimplement only after applicable approval. | +| Reusable product behavior | 20 | Hold the patient need/behavior pending contract, content, authorization, and UX approval. | +| Test/fixture only | 28 | Retain as synthetic test or provenance evidence; never ship or execute in production. | +| Rejected legacy behavior | 44 | Do not migrate legacy identity, packaging, assets, activation, or provisioning behavior. | +| **Total** | **134** | Every final-slice source has one exact class, decision, SHA-256, surface, and domain set. | + +No source is classified as “copy as-is.” Even a reusable safety primitive transfers only +as a requirement and testable property, never as implicit approval of legacy code, schema, +copy, identifier, endpoint, or operating policy. + +## 2. Bounded universe and reproducibility + +### 2.1 Included source roots + +The product-universe rule is code-owned in the builder and verifier. It includes tracked +files from: + +- `hummingbird/iosPatientApp/**`; +- `hummingbird/androidPatientApp/**`; +- `config/hummingbird-patient.php` and + `config/hummingbird-patient-content.php`; +- `routes/patient.php`; +- patient API controllers and requests; +- patient models, policies, contracts, and services; +- patient feature and unit tests; +- migrations whose filename contains `patient`, except the unrelated + `patient_flow` subsystem; and +- Hummingbird console commands whose class filename contains `Patient`. + +This scope deliberately follows product ownership, not filename proximity alone. It +includes native packaging and binary assets because bundle/package identity, test hooks, +release source sets, launcher resources, backup policy, and network policy are functional +release inputs. + +### 2.2 Explicit exclusions + +The following are not silently ignored: + +| Exclusion | Reason | +| ------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------- | +| `database/migrations/*patient_flow*.php` | These migrations belong to the staff/operational Patient Flow subsystem, not the Hummingbird Patient product. | +| General/staff mobile, web, and operational source roots | Outside this product universe. Staff communication sources needed for end-to-end handoff were explicitly covered earlier. | +| Generated build output and local emulator/tool caches | Untracked products of verification, not reviewable source. | +| Nightingale application source | Destination implementation, governed by its own boundary tests; it is not legacy migration input. | +| Production data or deployed configuration | Neither is required to classify tracked source and neither was accessed. | + +### 2.3 Inventory identity + +| Inventory | Count | SHA-256 path-list digest | +| --------------------------------- | ----: | ------------------------------------------------------------------ | +| Full legacy patient-product scope | 256 | `a307e1957df7ef78eb61a9a9123f3902fd8929ebb3aaeb4dce48f2c88fb4a881` | +| Final unclassified slice | 134 | `4301de88a9071214001c2a58aa8fbc624bb49f24bb566428c8a1ef98aa44c13d` | + +The digest is calculated over lexicographically sorted repository-relative paths with a +final newline. Every final-slice entry also carries the SHA-256 of the exact file bytes at +the reviewed commit. + +The verifier fails if: + +- a tracked product source is added or removed; +- a source is no longer covered by one of the three ledgers; +- the final slice overlaps a prior ledger; +- a path, byte checksum, surface, class, disposition, category, or decision changes; +- any decision rationale becomes non-substantive; +- any runtime/production permission becomes true; or +- a required finding is softened. + +### 2.4 Legacy release-path collision revalidation + +Current `main` introduced a release-path collision after the original classification: it +assigned `net.acumenus.nightingale` and the Nightingale display name to the historical +`hummingbird/iosPatientApp` target and placed a Nightingale export profile inside that +legacy root. That would have allowed two source trees to claim the same patient-product +identity and contradicted the independently governed Nightingale application boundary. + +Correction commit `85316fbc5794735a77a0a4fa0e0096e18db4240b` restores the legacy project, +generated project, and Info.plist to their exact pre-collision Hummingbird Patient +identities; removes its Nightingale export profile; points the Apple registry to +`nightingale/iosApp`, project `Nightingale.xcodeproj`, and scheme `Nightingale`; and keeps +the export profile under that independent root. Product-boundary CI now rejects any +registry reversal, legacy Nightingale identifier, misplaced export profile, malformed +export policy, or exported IPA bundle/build mismatch. + +Follow-up test correction commit `45ddf907c0f15e378b37a1b0726724e346cb29fd` +removes a competing manual Activity recreation from the Android pseudolocale harness and +waits for the platform-managed locale relaunch to become resumed. The complete API 35 +instrumentation suite passes 10/10. This changes one classified test-source checksum but +does not change the 256-path universe, 134-path final slice, any classification, +disposition, runtime permission, or activation decision. + +The only new path retained inside the legacy product universe is +`hummingbird/iosPatientApp/.gitignore`. It excludes generated Xcode, archive, derived-data, +and per-user artifacts and is classified as the +`generated_artifact_hygiene_principle`: a portable repository-safety objective, not +Nightingale runtime code or permission to reuse the legacy root. The corrected universe +therefore grows by one source, from 255 to 256, while all patient runtime behavior and +findings remain unchanged. + +## 3. Surface reconciliation + +### 3.1 Final-slice source surfaces + +| Surface | Sources | Primary concerns | +| ---------------------- | ------: | -------------------------------------------------------------------------------- | +| Backend commands | 4 | Draft workflows and command-accessible synthetic provisioning | +| Backend request | 1 | Account presentation/delivery preference shape | +| Backend models | 15 | Append-only history, authored facts, cursors, failures, review/release facts | +| Backend services | 14 | HMAC/audit, authored inputs, source reconciliation, draft/release, demo fixtures | +| Database migrations | 11 | Projection kinds, immutable history, preferences/goals, outbox, review/release | +| Backend tests | 9 | Synthetic provisioning, authored inputs, pathway history/source/release behavior | +| iOS packaging | 6 | Legacy project, scheme, Info.plist, ignore rules, and generation ownership | +| iOS source | 12 | App boot, shell, journeys, preferences, presentation, privacy | +| iOS assets | 11 | Legacy icon and scenic Hummingbird imagery | +| iOS unit/UI tests | 3 | Presentation/state/session behavior | +| Android packaging | 9 | Legacy package/version/build/wrapper/proguard ownership | +| Android source | 9 | App boot, journey shell, preferences, accessibility, privacy | +| Android debug source | 2 | Launch extras and full synthetic reference scenario | +| Android release source | 2 | Inert replacements for launch hooks and synthetic scenario | +| Android resources | 13 | Hummingbird imagery, icons, themes, strings, network/backup controls | +| Android tests | 12 | Auth/session/privacy/presentation/product-boundary and debug/release controls | +| Android asset evidence | 1 | Legacy asset provenance | +| **Total** | **134** | | + +### 3.2 Decision groups + +| Decision ID | Count | Class | Disposition | +| ----------------------------------------- | ------: | ------------------------- | -------------- | +| `pathway_draft_command_held` | 1 | Reusable product behavior | Held | +| `reference_provisioning_rejected` | 6 | Rejected legacy behavior | Reject | +| `account_preference_contract_held` | 1 | Reusable product behavior | Held | +| `append_only_audit_safety_principle` | 15 | Reusable safety primitive | Principle only | +| `patient_authored_input_behavior_held` | 4 | Reusable product behavior | Held | +| `projection_release_safety_principle` | 4 | Reusable safety primitive | Principle only | +| `pathway_source_contract_held` | 2 | Reusable product behavior | Held | +| `testing_only_projection_fixture` | 1 | Test/fixture only | Test only | +| `append_only_projection_schema_principle` | 11 | Reusable safety primitive | Principle only | +| `asset_provenance_evidence` | 1 | Test/fixture only | Test only | +| `generated_artifact_hygiene_principle` | 1 | Reusable safety primitive | Principle only | +| `legacy_packaging_identity_rejected` | 14 | Rejected legacy behavior | Reject | +| `test_fixture_evidence` | 24 | Test/fixture only | Test only | +| `debug_synthetic_fixture` | 2 | Test/fixture only | Test only | +| `native_journey_behavior_held` | 12 | Reusable product behavior | Held | +| `native_privacy_configuration_principle` | 3 | Reusable safety primitive | Principle only | +| `legacy_brand_assets_rejected` | 23 | Rejected legacy behavior | Reject | +| `native_presentation_safety_principle` | 6 | Reusable safety primitive | Principle only | +| `release_synthetic_exclusion_principle` | 2 | Reusable safety primitive | Principle only | +| `legacy_runtime_activation_rejected` | 1 | Rejected legacy behavior | Reject | +| **Total** | **134** | | | + +## 4. Patient-journey functional analysis + +### 4.1 Useful journey properties + +The legacy app contains strong candidate behavior that Nightingale should preserve as +requirements after its contract and content are approved: + +1. **Released-only posture.** Today, My Path, and Care Team empty states explicitly refuse + to infer content from staff-only or operational data. +2. **Uncertainty language.** Timing is described as estimated, being clarified, unknown, + or able to change rather than as a promise. +3. **Provenance visibility.** Individual cards often identify a patient-safe projection + source. +4. **Revision notices.** Updated released information can be distinguished from the + original patient view. +5. **Urgent-help separation.** The app repeatedly directs immediate needs to the bedside + call button or in-person staff and describes messages as non-urgent. +6. **Care-team role framing.** Care Team describes roles, responsibilities, availability, + and safe connection routes rather than exposing staff operational assignments. +7. **Authored-intent separation.** Personal goals and “what matters to you” messages do not + automatically mutate a clinical plan, order, consent, or assessment. +8. **Education clarification boundary.** Asking for an explanation does not represent + completion, comprehension, consent, or clinician assessment. +9. **Discharge uncertainty.** Released preparation material is not represented as a + discharge order or guaranteed date. +10. **Empty/error dignity.** Missing released content is explained without blaming the + patient or inventing clinical detail. + +These are product and safety inputs. They are not evidence that the current wording, +information architecture, source fields, state registry, or clinical release is approved +for Nightingale. + +### 4.2 Top-level information architecture conflicts with the charter + +Both legacy apps render four primary destinations: + +1. Today +2. My Path +3. Care Team +4. Messages + +The Nightingale charter requires three primary destinations—Today, My Path, and Care +Team—and communication entered from an allowed care-team or question flow. A top-level +Messages destination is therefore **not migration-approved**. + +Before implementation, product and clinical operations owners must decide: + +- which released item or care-team context creates a message entry point; +- whether a general non-urgent question entry point exists; +- what topic/routing context is preselected; +- how patients return to the originating care context; +- how unavailable, outside-hours, source-closed, and discharged states behave; and +- how the urgent-help escape remains visible without making the interface alarming. + +### 4.3 iOS context aggregation is not field-level provenance + +The iOS `PatientExperienceSnapshot.live` implementation: + +- selects the first encounter; +- takes the maximum `as_of` time across available projection envelopes; +- marks the aggregate stale if any projection is stale; +- concatenates provenance and uncertainty across projections; and +- feeds that shared context to several screens. + +This can make a screen’s freshness/source label describe a different projection than the +card being read. It can also make one stale auxiliary projection mark unrelated content +stale, while the maximum timestamp can appear newer than some displayed fields. + +Nightingale must bind context to the smallest patient-meaningful unit: + +| Required field-level context | Rule | +| ---------------------------- | --------------------------------------------------------------------------------------------- | +| Projection kind | Identify the governed source contract that produced the displayed content. | +| Source observation time | Time the authoritative source state was observed, not merely response or rendering time. | +| Generated/released time | Keep projection generation and release distinct. | +| Freshness status | Evaluate against a versioned, source-specific policy; do not derive from an unrelated panel. | +| Uncertainty | Bind to the exact fact or section whose certainty is described. | +| Revision/retraction | Identify what changed, when the replacement became effective, and what the patient should do. | +| Provenance label | Use approved patient language; never expose raw source-system or staff-only identifiers. | + +### 4.4 Android composite My Path context is incomplete + +Android renders pathway, pathway events, discharge readiness, and rounds summary in one My +Path destination. Its context map selects: + +1. pathway context when available; +2. otherwise pathway-events context; +3. otherwise discharge-readiness context; +4. otherwise rounds-summary context. + +The remaining subprojections can still be displayed below that one selected context. +Consequently, the My Path header does not prove the source, freshness, or uncertainty of +every section on the screen. + +Nightingale must either: + +- render context independently for each subprojection; +- define a governed composite envelope with coherent observation/release semantics; or +- split the material into patient-tested subroutes with their own context. + +Choosing the newest timestamp or first non-null envelope is prohibited. + +### 4.5 Encounter cardinality remains unsafe + +Both legacy native flows silently select the first encounter returned by the server. The +prior encounter-access classification established that the backend ordering is not a +patient-safe transfer/readmission selection rule. + +Nightingale’s held first-read candidate therefore remains correct: + +- zero eligible current contexts returns a bounded empty outcome; +- exactly one self context may proceed; +- more than one context fails closed to account review until an approved selector exists; +- representatives remain out of scope; and +- clients never select the first record by array position. + +### 4.6 Pathway release controls are strong but narrow + +The backend contains a meaningful two-person pathway release pattern: + +- an active authorized clinical reviewer records an immutable approved/withheld decision; +- a different active catalog release manager performs release; +- the draft must be version-pinned, current/aging, under an effective grant and policy; +- the release is a new immutable projection, not an in-place mutation; +- the release execution and projection are created transactionally; +- actor identities are stored as domain HMAC digests rather than raw staff IDs; and +- database triggers validate review/release facts and enqueue released-projection outbox + work. + +That control exists for the version-pinned **pathway** draft. It does not prove equivalent +two-person release coverage for Today, care team, pathway events, discharge readiness, +rounds summary, education, communication copy, or notification copy. Nightingale must not +generalize one pathway-specific service into global clinical approval. + +### 4.7 Patient-authored goal and preference semantics + +The backend distinguishes two concepts that must not be collapsed: + +| Concept | Legacy behavior | Nightingale requirement | +| ------------------------------------- | ---------------------------------------------------------------------- | ------------------------------------------------------------------------------------ | +| Account presentation preference | Updates principal-level display/delivery fields | Separate device presentation, locale, communication consent, and delivery preference | +| “What matters to you” care preference | Immutable content-free association to an encrypted accountable message | Keep nonclinical until a staff workflow explicitly evaluates it | +| Patient-authored personal goal | Immutable content-free association to an encrypted accountable message | Never silently become a care-plan goal, order, assessment, or completion fact | +| Released care-team goal | Read-only projection field with author/source context | Preserve author identity class and release provenance | + +The content-free association pattern is useful: it prevents plaintext preference/goal +content from being duplicated in a secondary table. It still depends on the unresolved +Nightingale communication workflow, idempotency model, responder ownership, review status, +and patient-visible outcome. + +## 5. Preference and accessibility reconciliation + +### 5.1 Server/native shape mismatch + +The legacy server accepts seven optional preference fields: + +| Server field | Server values | iOS editor | Android editor | Classification | +| ---------------------- | ---------------------------- | :--------: | :------------: | -------------- | +| `locale` | BCP-47-like string | No | No | Held | +| `timezone` | IANA timezone | No | No | Held | +| `text_size` | standard, large, extra_large | Yes | Yes | Principle only | +| `reduced_motion` | boolean | Yes | Yes | Split behavior | +| `high_contrast` | boolean | Yes | Yes | Principle only | +| `notification_preview` | hidden, generic | Yes | Yes | Held | +| `preferred_channel` | push, email, sms, none | push/email | push/email | Rejected as-is | + +Neither client can edit locale or timezone. Neither exposes `sms` or `none`. Both default a +missing preferred channel to `push`, even though the communication review proved that no +patient push-delivery implementation exists. + +Nightingale must not reproduce this mismatch. Every preference needs: + +- an accountable owner; +- a defined scope: account, device, encounter, facility, or message topic; +- a purpose and non-purpose; +- allowed values and unknown-value behavior; +- default and migration behavior; +- consent/withdrawal semantics where applicable; +- server and native parity; +- accessibility and language review; +- audit/retention policy; and +- proof that the represented capability actually exists. + +### 5.2 Device accessibility versus account preference + +Text size, reduced motion, contrast, and transparency are normally device/environment +properties. Persisting an account preference can be useful, but must never weaken stronger +system settings or imply that one patient’s device needs apply to another device. + +Observed positive behavior: + +- iOS uses the maximum of system Dynamic Type and the account-selected minimum; +- Android uses the maximum of system font scale and the account-selected scale; +- iOS combines system and account reduced-motion values; +- iOS combines increased contrast/reduced transparency with account preferences; and +- Android applies an account high-contrast choice to its theme/background policy. + +Required Nightingale rule: + +```text +effective presentation + = strongest applicable system accommodation + + explicitly supported device-local choice + + approved account preference only when it cannot weaken either +``` + +The product must document how conflicts resolve and which choices roam across devices. + +### 5.3 Android reduced motion is currently semantically inert + +The Android preference is decoded, saved, stored in +`PatientPresentationAccessibility`, and displayed in a “Your reading preferences” notice. +No main-source branch uses it to suppress, shorten, replace, or otherwise alter motion. + +This is not evidence that Android currently violates a motion animation—the reviewed +surface may simply contain little motion. It is evidence that the “Reduce motion” control +does not itself change presentation and therefore should not be shown as an effective +setting until a tested behavior exists. + +Nightingale acceptance must include: + +- an inventory of every animation, transition, shimmer, parallax, auto-scroll, and + decorative motion; +- system-level reduced-motion behavior; +- the product-choice behavior, if retained; +- no-motion alternatives; +- transition duration and repetition limits; +- screen-reader and focus stability; and +- emulator/device evidence that toggling the setting changes every governed motion site. + +### 5.4 iOS reduced motion has bounded functional effect + +iOS uses the combined system/account setting for: + +- privacy-cover transition and animation; and +- decorative scenic-background motion. + +This is reusable only as a control objective. Nightingale must enumerate and test every +future motion site; the existence of two compliant branches does not establish app-wide +coverage. + +### 5.5 Text scaling and layout evidence still needed + +Programmatically increasing text scale is not equivalent to proving accessibility. The +current source does not establish: + +- every screen at the largest system accessibility sizes; +- landscape/compact-height behavior where supported; +- no clipped or truncated clinical copy; +- focus order after reflow; +- minimum touch targets; +- language expansion; +- screen-reader headings/actions/values for every journey; +- contrast under light/dark/high-contrast modes; +- images disabled or withheld; +- external keyboard/switch access; or +- cognitive-load and plain-language acceptance by patients. + +These remain Stream E work, not inferred from code inspection. + +## 6. Synthetic and debug boundary + +### 6.1 Native compile-time separation + +The legacy native targets contain two useful release-safety patterns: + +| Platform | Debug behavior | Release behavior | +| -------- | ------------------------------------------------------------------------------ | ------------------------------------------------------------------------ | +| iOS | Synthetic activation and payload are inside `#if DEBUG`; UI tests use env keys | Synthetic request resolves false and synthetic payload is absent | +| Android | Debug source set supplies launch extras and a full marked synthetic scenario | Release source set supplies inert hooks and null synthetic-content stubs | + +The properties are reusable; the Hummingbird test keys, extras, IDs, content, names, and +payloads are not. + +Nightingale release evidence must prove: + +- debug/test symbols are absent from the signed Release binary/APK/AAB; +- release launch arguments, intents, deep links, notifications, restore state, and + accessibility/test APIs cannot activate a fixture; +- no synthetic patient name, encounter ID, message, token, or content remains in release + resources; +- test backends and certificates are excluded; +- store/distribution configuration cannot select a debug endpoint; and +- release analytics/crash reports cannot label synthetic content as a real patient. + +### 6.2 Backend testing-only fixture + +`SyntheticPatientProjectionProvisioner` refuses every environment except `testing` and +creates deterministic patient, grant, policy, cursor, and released-projection fixtures. +This is strong test containment and may inform a Nightingale test factory. + +It remains test-only because it: + +- constructs synthetic released clinical content; +- creates a synthetic active principal and grant; +- uses a testing policy and source system; +- bypasses real clinical/content approval by design; and +- is intended for automated verification, not patient use. + +No production or staging fixture should reuse those records or identifiers. + +### 6.3 Command-accessible reference provisioning is rejected + +The remaining reference commands and demo provisioners have several safeguards: + +- default-off configuration; +- PostgreSQL requirement; +- local-runtime refusal of a remote database; +- required synthetic `demo-`/`sim-` pseudonyms; +- command ownership checks; +- dry-run defaults; +- explicit `--commit`; +- pending/non-active principal constraints; +- secret redaction unless explicitly requested; +- draft-only projection state; +- synthetic provenance and unknown uncertainty; +- outbox/publication absence checks; and +- idempotency/locking behavior. + +They can nevertheless mutate a deployed application database when enabled and invoked. +They can create or bind an operational encounter, patient principal, identity link, grant, +enrollment challenges, one-time secrets, draft policy, cursors, and draft projections. + +Therefore: + +- they are rejected as Nightingale runtime or deployment tooling; +- they were not executed; +- their `--commit` paths were not tested against production; +- they do not authorize a sample patient; +- their one-time secret output is not a support/recovery design; +- their synthetic labeling does not make production mutation appropriate; and +- a future Nightingale non-production fixture system requires a separate isolated + environment, data lifecycle, owner, teardown, and approval. + +## 7. Release, packaging, and product-identity analysis + +### 7.1 Legacy packaging is not a migration primitive + +The legacy native packages remain Hummingbird Patient: + +- Hummingbird bundle/package identifiers; +- Hummingbird names and test targets; +- Hummingbird launcher/app icons and scenic assets; +- Hummingbird endpoint/activation keys; +- Hummingbird test hook names; and +- Hummingbird build/version ownership. + +Nightingale already has independent application roots and the separately supplied +nightingale icon. Copying a project, package, plist, Gradle build, resource tree, or wrapper +as the migration mechanism would reintroduce legacy identity and activation decisions. + +### 7.2 Runtime activation is not release governance + +iOS can activate the legacy API from Info.plist or a process environment value after URL +validation. Android compiles an enabled flag and base URL from Gradle properties, defaulting +disabled while retaining the legacy production URL as a build value. + +URL validation and default-off behavior are useful, but neither mechanism proves: + +- who authorized the build; +- which contract/schema/client version it uses; +- which environment and certificate policy is allowed; +- which cohort may receive it; +- whether identity/source/content/communication gates are approved; +- how the feature expires or rolls back; +- how minimum versions are enforced; or +- that the distributed binary matches the reviewed SHA. + +Nightingale requires a signed, immutable, environment-specific release manifest and +code-owned default denial. Environment variables or developer Gradle properties cannot be +the clinical/product authorization mechanism. + +### 7.3 Store and distribution evidence is absent + +The Android legacy target uses `versionCode = 1`, +`versionName = "0.1.0-pilot"`, and a non-minified Release build. The repository does not +prove external store records, distribution signing, retained released-artifact upgrade, +monotonic version history, privacy declarations, support URLs, notification identities, or +store review. + +Those facts are not defects in a reference app; they are evidence that the repository +cannot claim release readiness. Nightingale’s Stream B released-artifact/store item remains +open. + +### 7.4 Brand assets + +All final-slice Hummingbird Patient icon, scenic photo, color, string, and theme resources +are rejected as Nightingale assets. Their design techniques may inform independent +patient testing, but the files themselves do not transfer. + +Nightingale continues to use: + +- its separate product name and app IDs; +- the supplied nightingale artwork and checksum-pinned provenance; +- its own launcher/app-icon derivatives; +- its own no-data scenic foundation; and +- its own accessibility and product-boundary tests. + +## 8. Persistence, audit, and database controls + +### 8.1 Reusable principles + +The reviewed models/migrations demonstrate useful control objectives: + +- external UUID assignment separated from database IDs; +- append-only application models; +- database triggers that reject updates/deletes to governed facts; +- immutable pathway stage/milestone status events; +- projection cursors with source/version/observation state; +- explicit projection failure facts; +- content-free access audit events; +- immutable patient-authored goal/preference associations; +- draft versus released projection states; +- independent review and release-execution records; +- content digest and trace digest binding; and +- released-projection outbox enqueueing. + +Nightingale must reissue these controls only after its data model, retention, correction, +HIM/legal, audit, source, and release requirements are approved. + +### 8.2 Append-only does not mean clinically immutable + +An append-only database prevents silent rewriting; it does not prevent stale or incorrect +patient-visible content. Nightingale requires explicit: + +- correction and supersession links; +- effective/retracted time; +- patient acknowledgment where appropriate; +- downstream cache/notification invalidation; +- source reconciliation; +- audit of who decided and released the correction; +- patient-safe explanation; +- staff escalation when a correction cannot be delivered; and +- retention/legal-hold behavior. + +### 8.3 No migration approval + +The eleven migration files are classified as safety-principle evidence only. This review: + +- did not execute a migration; +- did not inspect deployed schema state; +- did not query production; +- did not create a backup; +- did not authorize a Nightingale schema; and +- does not satisfy the repository’s path-scoped production migration workflow. + +## 9. Cross-platform parity decisions + +| Concern | iOS legacy result | Android legacy result | Nightingale decision | +| ----------------------------- | -------------------------------------------------- | ---------------------------------------------------------------- | ------------------------------------------------------------------- | +| Primary destinations | Today, My Path, Care Team, Messages | Today, My Path, Care Team, Messages | Hold Messages as contextual-only; no top-level destination approval | +| Encounter selection | First returned encounter | First returned encounter | Reject; zero/one candidate and fail closed on multiple | +| Today empty state | Released-only, no staff-data inference | Released-only, no staff-data inference | Reimplement after Today contract approval | +| My Path composition | Several subprojections in one long view | Several subprojections in one long view | Patient-test hierarchy; field-level context required | +| Context/freshness | Aggregated across projections | Per destination, but composite path uses first available context | No unrelated max/first aggregation | +| Care-team connection guidance | Bedside plus nonurgent Messages when allowed | Bedside plus secure Messages when available | Contextual entry, accountable routing, urgent escape | +| Text-size accommodation | Max(system, selected minimum) | Max(system font scale, selected scale) | Preserve stronger system setting; verify reflow | +| High contrast | Account plus system contrast/transparency branches | Account theme/background branch | Reimplement and test app-wide | +| Reduced motion | Controls privacy/background transitions | Stored and announced; no rendering-control branch | Do not expose until behavior is testable on both platforms | +| Locale/timezone editor | Absent | Absent | Define ownership and parity before persistence | +| Delivery-channel editor | Push/email, missing defaults to push | Push/email, missing defaults to push | Reject until channels/consent/support exist | +| Synthetic release exclusion | Compiler-gated | Release source-set stubs | Independent binary/source-set exclusion proof | +| Product activation | Plist/environment + legacy host validation | Gradle properties + legacy host build value | Reject; signed Nightingale release manifest required | +| Product identity/assets | Hummingbird Patient | Hummingbird Patient | Reject; independent Nightingale identity/artwork only | + +## 10. Nightingale implementation gates derived from this review + +### 10.1 Journey contracts + +- [ ] Name the product, clinical safety, content, privacy, accessibility, language, source, + support, and release owners for each journey. +- [ ] Define separate held candidate envelopes for Today, My Path, Care Team, education, + pathway events, discharge readiness, and rounds summary. +- [ ] Specify every field’s source, version, release state, patient-language owner, + observation/generated/released times, freshness, uncertainty, correction, retraction, + offline, and unknown behavior. +- [ ] Define zero/one current inpatient context and reject client-side first-record + selection. +- [ ] Decide whether My Path remains composite or is split into patient-tested subroutes. +- [ ] Define field-level context for every displayed subprojection. +- [ ] Define the contextual communication entry points; keep Messages out of primary + navigation unless the charter is deliberately reapproved. +- [ ] Add complete omission, stale, corrected, retracted, dependency-outage, and + incompatible-version fixtures. +- [ ] Add patient-safe non-disclosure behavior for wrong patient, encounter, relationship, + grant, scope, and source state. + +### 10.2 Preferences and presentation + +- [x] Split device accessibility settings from account preferences. + Nightingale now stores only reduced-motion and decorative-imagery choices in + product-local platform stores; neither is connected to a care account, API, clinical + model, locale, communication consent, or delivery preference. +- [x] Decide whether any accessibility preference roams across devices. + Nightingale implements no account/cloud synchronization. Android backup and transfer + exclude shared preferences. Apple-managed iOS backup/restore behavior remains an + explicitly unratified external release property, not an app roaming feature. +- [ ] Define locale ownership, translation release, fallback, language-change, and + interpreter-support behavior. +- [ ] Define timezone derivation and prevent a stale account timezone from changing clinical + meaning. +- [ ] Remove delivery-channel choices until corresponding channels and consent/withdrawal + workflows exist. +- [ ] Define preview privacy independently for lock screen, notification center, wearable, + car, shared device, and representative access. +- [x] Implement actual reduced-motion behavior on both platforms before presenting the + control as effective. + Every current governed motion site consumes the stronger system/patient policy; iOS + removes the foundation transitions and Android uses a zero-duration `snap()` policy. +- [ ] Prove largest text, reflow, focus order, contrast, target size, screen reader, + landscape, language expansion, and images-disabled behavior. + The images-disabled foundation subset is proven on both platforms, but the remaining + compound requirements keep this item open. +- [x] Bind accessibility evidence to exact Debug and Release artifacts. + The presentation decision records SHA-256 values for the exact local iOS simulator + executables and Android APKs. Distribution signing, stores, protected-`main` release, + and independent approval remain separate open gates. + +### 10.3 Patient-authored inputs + +- [ ] Define Nightingale-owned personal-goal and “what matters” operations only after + communication ownership is approved. +- [ ] Preserve content encryption and content-free secondary associations. +- [ ] Preserve client operation identity through ambiguous network retries. +- [ ] Define staff receipt, ownership, review, response, closure, escalation, correction, and + discharge-mid-thread behavior. +- [ ] Never represent submission as a care-plan mutation, order, consent, assessment, + comprehension, or guaranteed team acknowledgment. +- [ ] Show exact patient-visible state based on accountable backend facts. + +### 10.4 Synthetic/test architecture + +- [ ] Create Nightingale-only synthetic factories under test/debug source boundaries. +- [ ] Use no legacy patient name, UUID, endpoint, hook, extra, environment key, source + identifier, policy, or content record. +- [ ] Prove release artifacts exclude every synthetic activation and payload. +- [ ] Isolate non-production integration data from operational/production sources. +- [ ] Define fixture ownership, expiry, teardown, collision detection, and audit. +- [x] Record the explicitly authorized 2026-07-27 production sample exception without + weakening the runtime boundary. One Nightingale synthetic operational encounter and + one pending/inactive, contactless/passwordless principal were cloned from only the + safe Hummingbird reference shape. No identity link, grant, challenge, session, + projection, content, route, client, or activation was created; the source record was + preserved and exact evidence is retained separately. + +### 10.5 Release and operations + +- [ ] Define signed environment-specific release manifests with exact contract, + vocabulary, content, identity, source, and feature versions. +- [ ] Reserve external Apple/Google identifiers through authorized organization accounts. +- [ ] Establish monotonic build/version policy and retained-artifact upgrade tests. +- [ ] Complete distribution signing, store records, privacy declarations, support contacts, + screenshots, release notes, and rollback. +- [ ] Prove exact-SHA CI and binary namespace/debug-hook scans. +- [ ] Keep deployment and migration separate, protected, path-scoped, backed up, and + independently approved. +- [ ] Require a cohort/expiry-bound pilot manifest and signed go/no-go record. + +## 11. Prioritized implementation sequence + +The fastest safe path is not to port all 134 sources. It is to resolve the smallest set of +decisions that unlock one coherent patient journey: + +### Phase 1 — Approve one read-only Today slice + +1. Approve identity and current-inpatient prerequisites already modeled by the held + candidate. +2. Define a Nightingale Today candidate envelope with no raw grant/source identifiers. +3. Bind every Today field to one approved source/release/freshness/uncertainty rule. +4. Define exact empty, stale, corrected, retracted, and unavailable outcomes. +5. Implement a default-off non-production adapter and exhaustive authorization fixtures. +6. Add Nightingale-native clients only after the contract is approved and generated. +7. Validate with patients, accessibility specialists, privacy/security, clinical safety, + nursing, language/interpreter, and support. + +### Phase 2 — Add My Path through governed composition + +1. Approve source adapter(s), pathway vocabulary, content release, and two-person release + applicability. +2. Decide composite versus subroute information architecture. +3. Add field-level context for pathway, events, discharge readiness, and rounds. +4. Implement correction/retraction and downstream invalidation. +5. Validate comprehension, anxiety, timing uncertainty, and care-team conversation impact. + +### Phase 3 — Add Care Team and contextual questions + +1. Approve patient-safe team roles and connection routes. +2. Approve contextual entry points and routing ownership. +3. Implement exact idempotency, staff projection, readback, and patient-visible states. +4. Add no-delivery/after-hours/downtime/discharge behavior. +5. Add push/email/SMS only as separately approved capabilities; do not expose placeholders. + +### Phase 4 — Expand accommodations and controlled pilot evidence + +1. Complete locale/language, interpreter, representative, shared-device, and accessibility + decisions. +2. Complete non-production integration, outage, recovery, security, and load exercises. +3. Produce exact signed artifacts, store/distribution evidence, rollback, and a + cohort/expiry-bound pilot manifest. +4. Deploy only from reviewed protected `main` after exact-SHA CI and all named approvals. + +## 12. Evidence and holds + +### Completed in this classification + +- Exact full-universe definition and path-list digest. +- Exact final-slice definition and path-list digest. +- SHA-256 for every one of 134 final-slice sources. +- One class, disposition, surface, decision, and domain set per source. +- Closure across all three classification ledgers. +- Patient-journey, preference, accessibility, synthetic/debug, persistence, and release + analysis. +- Required findings that preserve known cross-platform and governance gaps. +- Negative mechanical enforcement in CI. + +### Still held + +- Every Nightingale runtime operation and route. +- Identity provider, enrollment, recovery, representative access, and production source + adapter. +- Patient API client and Android network permission. +- Every clinical projection and content release. +- Every patient-authored mutation. +- Communication, notification provider, push, email, SMS, and background refresh. +- Locale/translation and account preference persistence. +- Synthetic runtime and deployed reference provisioning. +- Database migration and production data access. +- Distribution/store release, pilot, deployment, and activation. +- Named clinical, privacy/security, accessibility, patient-advisor, language/interpreter, + legal/HIM, nursing, medical-staff, pharmacy, support, and release approvals. + +## 13. Safety statement + +No production database was accessed. No patient, principal, identity link, grant, session, +encounter, preference, message, goal, pathway draft, review, release, outbox event, feature +flag, route, provider, migration, deployment, or pilot state was created, read, or changed. + +The command-accessible Hummingbird reference provisioners were reviewed from source and +explicitly rejected as Nightingale/production tooling. Their existence is not permission to +run them. diff --git a/docs/nightingale/MIGRATION-CLASSIFICATION-2026-07-26.md b/docs/nightingale/MIGRATION-CLASSIFICATION-2026-07-26.md new file mode 100644 index 00000000..ab458c6f --- /dev/null +++ b/docs/nightingale/MIGRATION-CLASSIFICATION-2026-07-26.md @@ -0,0 +1,103 @@ +# Nightingale migration classification — foundation slice + +**Status:** Complete source-level classification of the bounded 256-source legacy +Hummingbird Patient product universe. Classification does not approve any legacy source, +contract, route, client, model, migration, content, fixture, asset, activation mechanism, +or release configuration for Nightingale migration or production use. + +**Reference roots:** `hummingbird/iosPatientApp` and `hummingbird/androidPatientApp` + +**Destination roots:** `Nightingale/iosApp` and `Nightingale/androidApp` + +## Classification rules + +| Class | Meaning | Migration rule | +| ------------------------ | ---------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------ | +| Safety primitive | Platform control that can reduce disclosure risk without carrying patient data or legacy identity | Reimplement under the Nightingale namespace with independent tests | +| Product behavior | Patient-facing behavior that may be useful but depends on approved contracts, content, identity, or policy | Hold until its source, authorization, failure behavior, and patient-language review are approved | +| Test/fixture only | Synthetic scenario, preview hook, screenshot harness, or reference fixture | Never ship; retain only as evidence or recreate under an explicit non-production test namespace | +| Rejected legacy behavior | Staff coupling, old branding/identifier, ungoverned endpoint/storage, or behavior that cannot fail closed | Do not migrate | + +## Classified foundation sources + +| Reference source | Classification | Nightingale disposition | Evidence | +| -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `hummingbird/iosPatientApp/HummingbirdPatient/App/HummingbirdPatientApp.swift` | Mixed: lifecycle safety primitive plus legacy patient bootstrapping | Reimplemented only the scene-phase privacy cover; rejected legacy configuration, API client, token store, and patient view-model bootstrapping for this slice | `NightingaleApp.swift`; iOS UI privacy-cover test | +| `hummingbird/iosPatientApp/HummingbirdPatient/Privacy/PatientPrivacyCoverView.swift` | Safety primitive with legacy branding | Reissued as a Nightingale-only cover with new copy, identifiers, palette, and brand artwork | `NightingaleVisualFoundation.swift`; `nightingale-privacy-cover` UI assertion | +| `hummingbird/iosPatientApp/HummingbirdPatient/DesignSystem/PatientPhotoBackground.swift` | Mixed: accessibility-aware presentation primitive plus legacy scene/content taxonomy | Reimplemented only the accessibility-aware decorative-background policy. The Nightingale product now uses its independently supplied, seven-file governed catalog with exact shared derivatives, local-Gregorian-day selection, strong scrims, and no legacy scene names, species labels, Hummingbird content strings, or clinical semantics. | Background manifest/verifier, iOS resource/catalog tests, Release artifact scan, and simulator inspection; no clinical content present | +| `hummingbird/androidPatientApp/app/src/main/java/net/acumenus/hummingbird/patient/PatientPrivacyPolicy.kt` | Safety primitive | Reissued as `NightingalePrivacyPolicy`; mandatory `FLAG_SECURE` verified by unit and API 35 instrumentation tests | Black system screenshot plus secure-window assertion | +| `hummingbird/androidPatientApp/app/src/main/java/net/acumenus/hummingbird/patient/MainActivity.kt` | Mixed: lifecycle safety primitive plus legacy patient API/session bootstrapping | Reimplemented only `onPause`/`onResume` privacy-cover state and secure-window application; rejected API, credentials, device/session, and patient-state construction | Three Android instrumentation tests pass | +| `hummingbird/androidPatientApp/app/src/main/java/net/acumenus/hummingbird/patient/ui/PatientScenicBackground.kt` | Mixed: accessibility-aware presentation primitive plus legacy assets/scenes | Reimplemented a Nightingale-only, no-data decorative background using the independently supplied seven-file governed catalog rather than legacy assets/scenes. The same derivatives and local-Gregorian-day selection are used on iOS; patient and supported system suppression policies remove them without affecting meaning or action. | Manifest/verifier, JVM catalog/policy tests, exact Release APK asset scan, and API 35 semantic inspection | +| `hummingbird/androidPatientApp/app/src/main/java/net/acumenus/hummingbird/patient/ui/PatientApp.kt` | Product behavior plus lifecycle cover | Reimplemented only the privacy overlay; held all authentication, patient session, pathway, messaging, preferences, device-session, and synthetic-scenario branches | Nightingale foundation remains no-network and no-data | +| `hummingbird/iosPatientApp/HummingbirdPatient/Networking/PatientSecureStore.swift` | Mixed: platform protection primitive plus legacy credential schema | Rejected persisted access/refresh tokens and the Hummingbird service. Reimplemented a dormant Nightingale-only, device-only Keychain primitive for one future binding descriptor; no production caller or token API exists. | Signed iOS Simulator Keychain canary test verifies round trip, accessibility attribute, non-synchronization, and idempotent deletion | +| `hummingbird/androidPatientApp/app/src/main/java/net/acumenus/hummingbird/patient/data/PatientSecureStore.kt` | Mixed: protected-storage primitive plus legacy credential/device identity | Rejected `EncryptedSharedPreferences`, access/refresh/session token schema, durable device UUID, Hummingbird alias, and migration. Reimplemented a dormant Nightingale-only Android Keystore AES-GCM primitive with backup/transfer exclusions and explicit deletion results. | API 35 emulator tests prove encrypted canary round trip, GCM tamper rejection, key/ciphertext deletion, and idempotency | +| `hummingbird/iosPatientApp/HummingbirdPatient/Features/Messages/PatientMessagesView.swift` and `Features/Path/PatientPathView.swift` | Product behavior with local volatile text state | Held all patient-facing messaging, education, copy, routing, and send behavior. Reimplemented only a product-agnostic volatile-input lifecycle coordinator; the legacy local `@State` drafts do not explicitly clear when the app becomes inactive. | iOS unit tests cover every defined clearing reason; the app root clears on inactive/background transition | +| `hummingbird/androidPatientApp/app/src/main/java/net/acumenus/hummingbird/patient/ui/PatientMessagingPanel.kt` | Product behavior with a useful no-persistence comment but incomplete lifecycle clearing | Held all composer UI, topic, message, and send behavior. Reimplemented only a product-agnostic volatile-input state that clears on `onPause` and future identity/logout/recovery boundaries. | JVM boundary tests cover all clearing reasons; API 35 lifecycle test proves a synthetic draft is cleared | +| `docs/hummingbird/api-contract/hummingbird-patient.v1.yaml` | Governed compatibility input containing both candidate safety controls and unresolved legacy product decisions | Preserved unchanged. Its 23 paths and 25 operations are inventoried operation by operation, but none was renamed, copied, routed, generated, or approved for Nightingale. | Nightingale contract ownership/authorization matrix; empty/default-off contract verifier | +| `routes/patient.php` and the legacy patient middleware/policy/service chain | Mixed: useful layered authorization and non-disclosure patterns plus Hummingbird-specific flags, realm, scopes, and route ownership | Retained as backend evidence only. Nightingale now reserves an independent namespace but registers no route and rejects aliases/proxies/redirects to the legacy surface. Future operations must independently prove product, identity, relationship, grant, resource, release, mutation, audit, and response gates. | Route/compatibility ADR; CI asserts zero Nightingale paths, no registered route, and no usable server | +| `config/hummingbird-patient.php` | Mixed: default-off control inventory plus legacy identity, token, session, projection, messaging, and notification decisions | Retained as a control/hazard inventory. No Hummingbird flag, token name, ability, route, policy version, or production setting becomes a Nightingale default. | Source checksum and decisions recorded in contract matrix | +| `PatientAuthService`, patient auth controller/requests, patient principal/link/challenge/session models, identity migration, and auth/session tests | Mixed: useful transactional, rotation/reuse, ownership, nondisclosure, and audit evidence plus legacy email/password, token-family, device, realm, grant, and enrollment choices | Classified as identity/session/recovery evidence only. Nightingale adopts no provider, credential, refresh token, local account, model, route, middleware, or representative workflow. A held candidate pins self-only prerequisites and 64 synthetic cases while all activation and runtime selections remain off. | Identity/session/recovery/source candidate decision, exact source hashes, candidate JSON, fixtures, and negative verifier | +| `config/hummingbird-patient-content.php` and `PatientProjectionStateVocabulary.php` | Mixed: explicit code/label validation safety principle plus draft Hummingbird registry and split version binding | Candidate principles only. All 12 domains and 49 pairs remain held; Nightingale requires one version/checksum-bound registry, exact missing/unknown behavior, field ownership, translations, and named approvals. | Patient-state vocabulary classification records source hashes and domain reconciliation | +| iOS `PatientStateVocabulary.swift`, `PatientAPIModels.swift`, and related rendering switches | Mixed product behavior with incomplete registry ownership and closed-enum failure behavior | No code or label migrated. Eight versioned domains plus one separately implemented category domain were reconciled against backend/Android; absent-version compatibility, duplicated labels, and schema placement remain rejected. | Patient-state vocabulary classification; zero vocabulary code in Nightingale | +| Android `PatientStateVocabulary.kt`, `PatientApiModels.kt`, and related rendering switches | Mixed product behavior with incomplete field coverage and permissive string decoding | No code or label migrated. Nine versioned domains were reconciled; omitted Today subdocuments, duplicated labels, unknown-category behavior, and absent-version compatibility remain blockers. | Patient-state vocabulary classification; zero vocabulary code in Nightingale | +| `EncounterController`, `PatientEncounterAccessService`, grant policy/model/schema, and reference boundary test | Mixed: useful ownership/effective-window/no-source-ID principles plus overexposed grant internals and incomplete identity/source/cardinality/audit checks | Classified as evidence for the first read candidate. The Nightingale candidate returns only a separate opaque handle, supports zero/one self context, and records 42 held synthetic outcomes. Its design path is reserved, but no route, OpenAPI path, model, migration, or database access was added. | Encounter-access candidate decision, candidate JSON, fixture matrix, and CI verifier | +| `PatientCommunicationEncounterGuard`, `PatientCommunicationLifecycleReconciliationService`, `Encounter`, and the `prod.encounters` schema migration | Mixed: current-source safety checks plus legacy grant coupling and an operational status vocabulary that is not sufficiently constrained for patient authorization | Retained as source-analysis evidence. Nightingale defines an independent four-state source port; the unconfigured implementation performs no query and returns `unavailable`. No `prod.encounters` adapter or status mapping is approved. | Route/compatibility/identity/source ADR; default-deny verifier and truth-table tests | +| Nightingale identity and current-inpatient candidate JSON/fixtures | New, synthetic-only non-runnable governance evidence | Defines 64 identity/session/recovery and 42 source-integrity cases, exact fail-closed outcomes, self-only scope, unresolved freshness, and approval gates. It creates no route, binding, query, provider, credential, identifier, patient data, or authorization. | Dependency-free verifier with nine negative mutations; CI-enforced enum/config/foundation reconciliation | +| `config/nightingale.php` and `app/Nightingale/{Identity,Inpatient,EncounterAccess,Disclosure,Activation}` | New Nightingale safety foundation | Code-owned, non-environment-activatable reservation and request-scoped ports. Identity/source defaults are unavailable. Generic non-disclosure collapses 39 of 40 request-state combinations to one exact failure tuple. Five separately typed approval/content/feature/pilot/source gates hold 31 of 32 activation-state combinations; the sole positive row only continues to later operation-specific release evaluation. Neither gate grants access, returns data, queries a source, or has a runtime caller. | [Generic non-disclosure foundation](./GENERIC-NON-DISCLOSURE-FOUNDATION-2026-07-27.md), [activation separation](./NAMESPACE-AND-ACTIVATION-SEPARATION-FOUNDATION-2026-07-27.md), independent verifiers, and exhaustive PHPUnit truth tables | +| Nightingale native namespace manifest, builder, and verifier | New product-identity and persistence-boundary evidence | Pins ten shared accessibility identifiers, four Debug hooks, ten fully qualified storage identifiers, zero telemetry events, and zero diagnostic channels. Legacy Hummingbird patient tokens, unnamespaced persistence, unregistered hooks, runtime logging, and manifest/source drift fail closed. This records current foundation identity only and does not approve telemetry, diagnostics, credentials, migration, signed distribution, or live patient access. | [Namespace decision](./NAMESPACE-AND-ACTIVATION-SEPARATION-FOUNDATION-2026-07-27.md), generated manifest, deterministic builder, source scanner, eight negative mutations, and native Release-artifact checks | +| iOS `PatientEncounterHandle`/`PatientAppViewModel` and Android `PatientEncounter`/`PatientSessionCoordinator` | Product behavior with raw authorization fields and unsafe first-row selection | Rejected as implementation. Future Nightingale clients must not receive grant UUID/scopes/dates/version or silently select the first row; more than one eligible context fails closed until an approved patient selector exists. | Candidate field matrix and native requirements; Nightingale apps remain offline and unchanged | +| 65 exact identity-input, enrollment/recovery, first-read, and error/non-disclosure sources across contract/backend/database/iOS/Android/tests | Mixed evidence containing reusable safety principles, held product decisions, test techniques, and rejected legacy behavior | Individually SHA-256 pinned. Confirms recovery is vocabulary rather than a complete workflow; rejects email/password and seven-field invitation defaults, legacy credentials/device identity, silent first-row selection, raw-scope fan-out, random UUID substitution, server-message passthrough, and projection-absence conflation. All runtime and production permissions remain false. | Detailed classification, machine-readable ledger, and negative verifier | +| 130 exact patient-communication, staff-handoff, notification, native-experience, urgency/offline, and error sources across contract/backend/database/patient iOS and Android/staff iOS, Android, and web/tests | Mixed evidence containing strong accountable-message principles, unapproved product/operations behavior, absent provider delivery, native retry hazards, and cross-platform state drift | Individually SHA-256 pinned. Reimplement current relationship checks, encrypted immutable content, content-free routing, replay locks, append-only receipts, and accountable responsibility only after approval. Hold all topics/copy/routes/states/providers/channels. Reject acceptance-as-delivery wording and retry-ID regeneration; pin absent patient push/automatic refresh, iOS escalation decode failure, Android state-label gaps, and the disproved staff-close decode hypothesis. | Detailed communication/notification classification, checksum ledger, ten required findings, and negative verifier | +| Final 134 exact journey, preference, accessibility-presentation, synthetic/debug, persistence, and release sources | 42 reusable safety primitives, 20 held product behaviors, 28 test/fixture-only sources, and 44 rejected legacy behaviors | Individually SHA-256 pinned. Rejects legacy product assets/packaging/activation and deployed reference provisioning; holds journey/preferences; records direct Messages navigation, iOS aggregate-context and Android composite-path context hazards, server/native preference mismatch, inert Android reduced-motion behavior, compile-time synthetic exclusion, repository artifact hygiene, and narrow pathway-only two-person release coverage. The three ledgers mechanically cover all 256 product sources. | Detailed final-slice classification, checksum ledger, full-universe closure verifier, and ten negative mutations | +| Nightingale Today candidate JSON/fixtures | New, synthetic-only non-runnable governance evidence | Replaces the legacy composite identifier/aggregate-context boundary with an opaque held path, eight explicit sections, field-level release/freshness/uncertainty/language/correction/offline context, and 68 deterministic outcomes. It does not approve or implement a route, adapter, source query, clinical field, generated client, patient, or activation. | [Today candidate decision](./TODAY-PROJECTION-CANDIDATE-DECISION-2026-07-26.md), 14 direct checksums, independent verifier, and 24 negative mutations | +| Nightingale patient-journey reference JSON/fixtures | New, synthetic-only non-runnable cross-surface acceptance evidence | Defines deterministic admission, transfer, procedure, test/result, communication/handoff, discharge, identity, representative, language/interpreter, accommodation, sensitive-data, outage/staleness, and correction/retraction expectations. It adds zero operations and keeps every runtime, patient, source, communication, notification, production, and deployment permission false. | [Patient-journey reference decision](./PATIENT-JOURNEY-REFERENCE-SCENARIO-CANDIDATE-DECISION-2026-07-27.md), 15 families, 27 cases, 12 source checksums, and 23 negative mutations | + +## Classification complete; implementation remains held + +Every source in the bounded 256-source legacy Hummingbird Patient product universe is now +classified. None is approved for blind copy, runtime activation, production replay, or +implicit parity. The protected-state slice is governed by +[the identity, recovery, and protected-state decision](./IDENTITY-RECOVERY-AND-PROTECTED-STATE-DECISIONS-2026-07-26.md). +Its implementation is dormant and verified with synthetic canaries only; it does not define +or persist a credential. + +The identity-input, enrollment/recovery, first-read, and error/non-disclosure slice is now +governed by the +[detailed 65-file classification](./IDENTITY-INPUT-FIRST-READ-ERROR-SOURCE-CLASSIFICATION-2026-07-26.md) +and its +[machine-readable checksum ledger](./migration/candidates/v0/source-classification.json). +That bounded slice is complete, but it does not approve runtime adoption. The contract +boundary is governed by +[the Nightingale contract ownership and authorization matrix](./CONTRACT-OWNERSHIP-AND-AUTHORIZATION-MATRIX-2026-07-26.md) +and its machine-verifiable, zero-operation artifact. The route, compatibility, identity, and +inpatient-source foundation is governed by +[its ADR](./ROUTE-COMPATIBILITY-IDENTITY-SOURCE-ADR-2026-07-26.md). The vocabulary review is governed by +[the patient-state vocabulary classification](./PATIENT-STATE-VOCABULARY-CLASSIFICATION-2026-07-26.md). +The communication and notification slice is governed by the +[detailed 130-file classification](./COMMUNICATION-AND-NOTIFICATION-SOURCE-CLASSIFICATION-2026-07-26.md) +and its +[checksum ledger](./migration/candidates/v0/communication-notification-source-classification.json). +It completes source classification for that bounded slice, not communication or notification +implementation. The remaining source inventory is governed by the +[journey, preference, presentation, synthetic, and release classification](./JOURNEY-PREFERENCE-PRESENTATION-RELEASE-SOURCE-CLASSIFICATION-2026-07-26.md) +and its +[134-source checksum ledger](./migration/candidates/v0/journey-preference-presentation-release-source-classification.json). +The verifier proves the union of the three ledgers covers all 256 product sources. None of +the records approves an operation or label. API clients, +authentication, recovery, clinical projections, care-team communication, preferences +persistence, notification delivery, and synthetic patient scenarios remain held. The first +read-only encounter-access candidate now has a decision record and verified synthetic +fixtures, but it remains outside the executable contract. Its namespace and path are +reserved only; no route or native network client may be added until the pre-operation gates +are independently satisfied. The first Today candidate likewise has a decision record and +68 verified synthetic outcomes. It requires field-level context and explicit +released-empty versus not-available behavior, but it remains non-runnable and does not +satisfy identity, source, clinical/content, language, privacy/security, accessibility, or +patient-advisor approval. The cross-surface patient-journey reference candidate adds 27 +synthetic cases across all 15 required scenario families. It makes transition and +non-disclosure expectations testable, but it does not approve or implement Today, My Path, +Care Team, communication, notification, representative access, clinical content, or any +production behavior. + +No production database, patient, grant, session, release, feature flag, or deployment state +was read or changed during this classification. diff --git a/docs/nightingale/NAMESPACE-AND-ACTIVATION-SEPARATION-FOUNDATION-2026-07-27.md b/docs/nightingale/NAMESPACE-AND-ACTIVATION-SEPARATION-FOUNDATION-2026-07-27.md new file mode 100644 index 00000000..a7bc3254 --- /dev/null +++ b/docs/nightingale/NAMESPACE-AND-ACTIVATION-SEPARATION-FOUNDATION-2026-07-27.md @@ -0,0 +1,324 @@ +# Nightingale namespace and activation-separation foundation + +**Date:** 2026-07-27 + +**Status:** bounded engineering implementation; no route, identity, source query, patient +content, disclosure, mutation, pilot, or production activation + +**Product:** Nightingale + +**Executable API paths:** 0 + +**Runtime callers:** 0 + +## 1. Decision + +Nightingale now has two mechanically enforced foundations that close separate product-plan +requirements without pretending that live patient functionality has been approved: + +1. every current product identifier is owned by the Nightingale namespace; and +2. clinical approval, patient-content release, feature activation, pilot enrollment, and + source-connector deployment are distinct, conjunctive prerequisites that default to + negative states. + +Neither foundation is an authorization or release mechanism. The namespace contract +prevents cross-product identity drift. The activation gate prevents any one governance +state from standing in for the other four. Even its sole all-positive outcome only +continues to a future operation-specific release evaluation. + +## 2. Scope and non-scope + +### 2.1 Implemented + +- exact iOS and Android product, bundle, package, accessibility, test-hook, protected-state, + presentation-preference, telemetry, and diagnostic namespace inventories; +- fully qualified Nightingale storage identifiers, including the formerly container-local + Android preference keys and both native future-session-binding keys; +- a deterministic namespace manifest builder; +- an independent source scanner and manifest verifier with eight negative mutations; +- five independent backend activation state types; +- a pure five-input activation gate; +- code-owned, non-environment-activatable negative defaults; +- five additional false activation fields in the empty executable contract; +- exhaustive 32-row PHPUnit and dependency-free truth-table proof; +- runtime-registration scans for the activation gate; and +- CI integration through both the contract job and every native product-boundary pass. + +### 2.2 Deliberately not implemented + +- an identity provider, proofing, enrollment, recovery, or representative workflow; +- a Nightingale route, controller, service-provider binding, middleware, or client; +- a production source adapter or patient query; +- a clinical approval repository or approval-record validator; +- a patient-content release repository or content payload; +- a feature-switch provider; +- a runtime pilot-manifest repository/evaluator, cohort membership service, consent + service, or support schedule (a later generated, non-runnable configuration candidate is + documented in + [`CONTROLLED-PILOT-MANIFEST-FOUNDATION-2026-07-27.md`](./CONTROLLED-PILOT-MANIFEST-FOUNDATION-2026-07-27.md)); +- a source-connector deployment or health evaluator; +- a disclosure, mutation, message, notification, telemetry event, diagnostic channel, or + log sink; or +- a claim that the all-positive gate result authorizes access, release, production, or + pilot use. + +## 3. Namespace contract + +### 3.1 Canonical rules + +| Identifier class | Canonical rule | Current inventory | +| ------------------------- | ------------------------------------------- | ----------------: | +| Product reverse-DNS root | `net.acumenus.nightingale` | 1 | +| Accessibility/test tags | prefix `nightingale-` | 10 | +| Debug-only native hooks | prefix `NIGHTINGALE_` | 4 | +| Persistent identifiers | prefix `net.acumenus.nightingale.` | 10 | +| Telemetry event names | prefix reserved; no event is implemented | 0 | +| Diagnostic channel names | prefix reserved; no channel is implemented | 0 | +| Legacy Hummingbird tokens | prohibited from Nightingale runtime sources | 0 | + +The generated source of record is +[`namespace/foundation-namespace.v1.json`](./namespace/foundation-namespace.v1.json). +It binds 12 exact source files to SHA-256 digests and records what each file is responsible +for. + +### 3.2 Accessibility and semantic identifiers + +Both native implementations expose the same exact ten identifiers: + +1. `nightingale-safe-shell` +2. `nightingale-product-heading` +3. `nightingale-foundation-mission` +4. `nightingale-privacy-status-heading` +5. `nightingale-display-comfort-heading` +6. `nightingale-reduce-motion-toggle` +7. `nightingale-motion-status` +8. `nightingale-hide-imagery-toggle` +9. `nightingale-imagery-status` +10. `nightingale-privacy-cover` + +The verifier extracts iOS `.accessibilityIdentifier(...)` and Android `.testTag(...)` +declarations from production sources. A manifest-only edit cannot conceal source drift. + +### 3.3 Debug-only test hooks + +The iOS foundation has exactly four current hooks: + +| Hook | Purpose | Release posture | +| ------------------------------------------------- | ---------------------------------------- | --------------- | +| `NIGHTINGALE_SHOW_PRIVACY_COVER` | Exercise lifecycle privacy cover | stripped | +| `NIGHTINGALE_TEST_ACCESSIBILITY_TEXT_SIZE` | Exercise accessibility text layout | stripped | +| `NIGHTINGALE_TEST_LAYOUT_DIRECTION` | Exercise right-to-left layout | stripped | +| `NIGHTINGALE_TEST_RESET_PRESENTATION_PREFERENCES` | Reset synthetic presentation preferences | stripped | + +The existing Release-artifact verifiers remain responsible for proving that these Debug +hooks do not survive in distributable binaries. The namespace verifier prevents an +unregistered or cross-product hook from entering the current sources. + +### 3.4 Persistent identifiers + +| Platform | Kind | Identifier | +| -------- | ---------------- | -------------------------------------------------------------------- | +| iOS | Keychain service | `net.acumenus.nightingale.protected-state.v1` | +| iOS | Keychain account | `net.acumenus.nightingale.protected-state.v1.future-session-binding` | +| iOS | Preference key | `net.acumenus.nightingale.presentation.v1.reduce-motion` | +| iOS | Preference key | `net.acumenus.nightingale.presentation.v1.hide-decorative-imagery` | +| Android | Preference file | `net.acumenus.nightingale.presentation.v1` | +| Android | Preference key | `net.acumenus.nightingale.presentation.v1.reduce-motion` | +| Android | Preference key | `net.acumenus.nightingale.presentation.v1.hide-decorative-imagery` | +| Android | Keystore alias | `net.acumenus.nightingale.protected-state-key.v1` | +| Android | Preference file | `net.acumenus.nightingale.protected-state-ciphertext.v1` | +| Android | Preference key | `net.acumenus.nightingale.protected-state.v1.future-session-binding` | + +The protected-state primitives remain caller-free and credential-agnostic. The +presentation keys contain device-local comfort choices only. Backup, device transfer, +cloud sync, account sync, and production credential persistence remain prohibited. + +### 3.5 Product strings, telemetry, and diagnostics + +The existing exact 15-key cross-platform copy verifier remains the patient-string +authority. This namespace slice adds an independent prohibition against +`net.acumenus.hummingbird`, `hummingbird.patient`, or `Hummingbird Patient` in Nightingale +runtime sources. + +No telemetry implementation or diagnostic channel currently exists. Their future +reverse-DNS roots are reserved, but both inventories remain empty and both implementation +flags remain false. Runtime logging and common analytics/crash primitives remain rejected +by the product-boundary scanner. + +## 4. Activation separation + +### 4.1 Independent typed states + +| Gate | Negative/default state | Positive prerequisite state | What the positive state does not prove | +| --------------------------- | ---------------------- | --------------------------- | ----------------------------------------------------------- | +| Institutional clinical | `absent` | `recorded` | content release, activation, enrollment, source, disclosure | +| Patient-content release | `unreleased` | `released` | clinical approval, activation, enrollment, source, access | +| Product feature activation | `disabled` | `enabled` | approval, content release, enrollment, source, access | +| Pilot enrollment | `not_enrolled` | `enrolled` | approval, release, consent validity, identity, access | +| Source connector deployment | `undeployed` | `deployed` | source health, correctness, release, identity, access | + +Each state has a distinct PHP type. A caller cannot accidentally pass a content-release +state where a pilot-enrollment state is required. + +### 4.2 Default configuration + +`config/nightingale.php` is code-owned and contains no environment lookup. Its activation +record is exactly: + +| Field | Default | +| -------------------------- | -------------- | +| `clinical_approval_state` | `absent` | +| `clinical_approval_record` | `null` | +| `content_release_state` | `unreleased` | +| `content_release_id` | `null` | +| `feature_activation_state` | `disabled` | +| `pilot_enrollment_state` | `not_enrolled` | +| `source_connector_state` | `undeployed` | + +The empty executable contract independently pins five corresponding Boolean activation +facts to `false`: + +- `clinical_approval_recorded` +- `patient_content_released` +- `feature_activated` +- `pilot_enrollment_confirmed` +- `source_connector_deployed` + +These are additive to the existing route, network, identity, source-query, disclosure, +mutation, and production false fields. + +### 4.3 Exhaustive truth table + +For compactness, `1` means the positive prerequisite state and `0` means its negative +state. Columns are clinical approval (`A`), content release (`C`), feature activation +(`F`), pilot enrollment (`P`), and source connector (`S`). + +| Row | A | C | F | P | S | Disposition | +| --: | --- | --- | --- | --- | --- | --------------------------------------------------- | +| 1 | 0 | 0 | 0 | 0 | 0 | `hold` | +| 2 | 0 | 0 | 0 | 0 | 1 | `hold` | +| 3 | 0 | 0 | 0 | 1 | 0 | `hold` | +| 4 | 0 | 0 | 0 | 1 | 1 | `hold` | +| 5 | 0 | 0 | 1 | 0 | 0 | `hold` | +| 6 | 0 | 0 | 1 | 0 | 1 | `hold` | +| 7 | 0 | 0 | 1 | 1 | 0 | `hold` | +| 8 | 0 | 0 | 1 | 1 | 1 | `hold` | +| 9 | 0 | 1 | 0 | 0 | 0 | `hold` | +| 10 | 0 | 1 | 0 | 0 | 1 | `hold` | +| 11 | 0 | 1 | 0 | 1 | 0 | `hold` | +| 12 | 0 | 1 | 0 | 1 | 1 | `hold` | +| 13 | 0 | 1 | 1 | 0 | 0 | `hold` | +| 14 | 0 | 1 | 1 | 0 | 1 | `hold` | +| 15 | 0 | 1 | 1 | 1 | 0 | `hold` | +| 16 | 0 | 1 | 1 | 1 | 1 | `hold` | +| 17 | 1 | 0 | 0 | 0 | 0 | `hold` | +| 18 | 1 | 0 | 0 | 0 | 1 | `hold` | +| 19 | 1 | 0 | 0 | 1 | 0 | `hold` | +| 20 | 1 | 0 | 0 | 1 | 1 | `hold` | +| 21 | 1 | 0 | 1 | 0 | 0 | `hold` | +| 22 | 1 | 0 | 1 | 0 | 1 | `hold` | +| 23 | 1 | 0 | 1 | 1 | 0 | `hold` | +| 24 | 1 | 0 | 1 | 1 | 1 | `hold` | +| 25 | 1 | 1 | 0 | 0 | 0 | `hold` | +| 26 | 1 | 1 | 0 | 0 | 1 | `hold` | +| 27 | 1 | 1 | 0 | 1 | 0 | `hold` | +| 28 | 1 | 1 | 0 | 1 | 1 | `hold` | +| 29 | 1 | 1 | 1 | 0 | 0 | `hold` | +| 30 | 1 | 1 | 1 | 0 | 1 | `hold` | +| 31 | 1 | 1 | 1 | 1 | 0 | `hold` | +| 32 | 1 | 1 | 1 | 1 | 1 | `continue_to_operation_specific_release_evaluation` | + +The one continuing row is not a release decision. A future operation must still pass the +identity, current-encounter, relationship, resource, field-release, freshness, language, +correction, audit-before-disclosure, serialization, and generic non-disclosure controls +applicable to that operation. + +## 5. Mechanical enforcement + +### 5.1 Namespace builder and verifier + +```bash +node scripts/ci/build-nightingale-namespace-foundation.mjs . --write +node scripts/ci/verify-nightingale-namespace-foundation.mjs . --self-test +``` + +The verifier: + +- requires byte-for-byte equality with deterministic builder output; +- validates exact product and namespace rules; +- scans current native accessibility/test-tag declarations; +- scans current Debug-hook declarations; +- proves every storage identifier uses the Nightingale reverse-DNS prefix; +- proves both telemetry and diagnostic inventories are empty; +- rejects legacy Hummingbird patient tokens; +- rejects unregistered logging/analytics primitives; +- verifies all 12 source SHA-256 values; and +- rejects eight adversarial manifest mutations. + +### 5.2 Backend verifier and tests + +```bash +php scripts/ci/verify-nightingale-backend-foundation.php . --self-test +php artisan test tests/Unit/Nightingale/NightingaleBackendFoundationTest.php +``` + +The independent verifier and PHPUnit suite each enumerate all 32 activation combinations, +require exactly 31 holds and one limited continuation, pin all state vocabularies, validate +the negative configuration, and confirm that no provider, route, or bootstrap source +registers the activation gate. + +### 5.3 Native artifact and emulator regression + +The existing native product-boundary verifier now invokes the namespace verifier. Release +artifact checks continue to prove exact bundle/package identity and absence of Debug hooks. +The accepted implementation record retains these exact results: + +| Platform/result | Accepted result | +| -------------------------------------------------- | ------------------------------------------- | +| iOS Debug build-for-testing | passed from a fresh `/tmp` DerivedData root | +| iOS unit suite, iPhone 16e / iOS 26.3.1 | 11 passed; 0 failed; 0 skipped | +| iOS UI suite, iPhone 16e / iOS 26.3.1 | 6 passed; 0 failed; 0 skipped | +| iOS unsigned Release build and artifact verifier | passed | +| Android Debug unit suite | 8 passed; 0 failed; 0 errors; 0 skipped | +| Android Release unit suite | 8 passed; 0 failed; 0 errors; 0 skipped | +| Android Debug/Release lint and assembly | passed | +| Android unsigned Release APK boundary | passed | +| Android isolated cold/wiped API 35 instrumentation | 10 passed; 0 failed; 0 errors; 0 skipped | + +The Android device was a temporary `nightingale-codex` AVD on its own port because a +separate process owned the shared `hb` AVD. The separate emulator was preserved. The +temporary AVD and the iPhone simulator were shut down after XML/xcresult reconciliation. +These results authorize completion of only the two bounded master-checklist items described +by this document. + +## 6. Failure behavior and rollback + +- Any manifest/source mismatch fails CI. +- Any unknown accessibility ID, test hook, storage key, telemetry event, or diagnostic + channel fails CI. +- Any legacy product namespace in Nightingale runtime source fails CI. +- Any single missing activation prerequisite returns `hold`. +- Any configuration attempt to change a default through an environment hook fails CI. +- Any service-provider, route, or bootstrap registration of the activation gate fails CI. + +Rollback is source-only because this slice adds no migration, route, deployment, account, +patient data, external identifier, or background task. The namespaced protected-state +keys remain dormant and caller-free. No credential migration or production-state rewrite +is required or authorized. + +## 7. Residual requirements + +This bounded foundation does not satisfy the open requirements for: + +- a live patient API or approved state vocabulary; +- named clinical, content, patient-advisor, accessibility, privacy, security, legal, or + operational approval; +- full WCAG 2.2 AA conformance or assistive-technology validation; +- production-like integration, source outage, failover, load, recovery, or rollback + exercises; +- a signed release, store record, notification surface, or upgrade test; +- a pilot manifest, enrollment decision, or support procedure; or +- a production deployment or activation. + +Those items remain independently open in the +[master Nightingale plan](../plans/nightingale-patient-product-2026-07-26.md). diff --git a/docs/nightingale/PATIENT-JOURNEY-REFERENCE-SCENARIO-CANDIDATE-DECISION-2026-07-27.md b/docs/nightingale/PATIENT-JOURNEY-REFERENCE-SCENARIO-CANDIDATE-DECISION-2026-07-27.md new file mode 100644 index 00000000..398d5e92 --- /dev/null +++ b/docs/nightingale/PATIENT-JOURNEY-REFERENCE-SCENARIO-CANDIDATE-DECISION-2026-07-27.md @@ -0,0 +1,469 @@ +# Nightingale patient-journey reference-scenario candidate decision + +**Status:** Held, synthetic, non-runnable safety and parity specification only + +**Decision date:** 2026-07-27 + +**Reviewed source commit:** `a825db89ec1efaf4b2c55a26e04d41161445b001` + +**Coverage:** 15 required journey families, 27 synthetic cases, zero executable +operations + +**Runtime status:** No OpenAPI path, Laravel route, controller, provider, identity +integration, source adapter, database query, generated client, native network request, +patient or representative record, clinical content release, communication mutation, +notification delivery, migration, deployment, pilot, or production activation is +authorized or present. + +## 1. Artifacts and reproducibility + +- [Candidate policy and evidence record](./api-contract/candidates/patient-journeys/v0/candidate.json) +- [Synthetic reference fixtures](./api-contract/candidates/patient-journeys/v0/fixtures.json) +- [Empty/default-off Nightingale contract](./api-contract/nightingale-foundation.v0.json) +- [Encounter-access candidate decision](./ENCOUNTER-ACCESS-CANDIDATE-DECISION-2026-07-26.md) +- [Today projection candidate decision](./TODAY-PROJECTION-CANDIDATE-DECISION-2026-07-26.md) +- [Communication and notification classification](./COMMUNICATION-AND-NOTIFICATION-SOURCE-CLASSIFICATION-2026-07-26.md) +- [Journey/preference/presentation/release classification](./JOURNEY-PREFERENCE-PRESENTATION-RELEASE-SOURCE-CLASSIFICATION-2026-07-26.md) + +The artifacts are generated by +`scripts/ci/build-nightingale-patient-journey-reference-candidate.mjs` and independently +checked by +`scripts/ci/verify-nightingale-patient-journey-reference-candidate.mjs`. +The verifier reproduces both checked-in artifacts from the deterministic builder and runs +23 adversarial mutations in CI. + +The exact source inventory contains 12 repository artifacts and has SHA-256 digest +`aed369c71dd22596ed09c908746e529617c3a62e1c92cfea5cb595895245ffea`. +The canonical fixture serialization has SHA-256 digest +`525c6f9264a14687b906485fcd105dd689c41e207785355ebf4f31a38f569de9`. +Every source byte, source order, family, case, policy field, and fixture digest is +mechanically checked. + +## 2. Decision outcome + +Nightingale needs one cross-surface reference-scenario catalog before individual Today, +My Path, Care Team, education, discharge, or communication features can be approved. +Without that catalog, independently implemented endpoints and native screens can each pass +focused tests while producing contradictory patient outcomes at transitions such as +transfer, discharge, identity correction, or content withdrawal. + +This candidate therefore specifies deterministic outcomes for the required journey +families while deliberately adding no operation. It establishes these rules: + +1. **A source event is not a patient release.** Admission, transfer, procedure, test, + result, discharge, identity, or staffing facts do not become patient-visible merely + because a source recorded them. +2. **Every value is independently governed.** Release, freshness, uncertainty, + correction, language, and offline behavior remain field-level decisions. A document + timestamp cannot make every field current. +3. **Unavailable, released-empty, and denied are not interchangeable.** The patient may + receive different safe presentation states, but authorization failures must remain + non-disclosing. +4. **Correction replaces and retraction withholds.** A prior value is never serialized + alongside its replacement. A retracted value, actor, reason, source, and internal + target remain hidden. +5. **Server acceptance is not staff delivery or review.** A future communication state may + say that the server durably accepted an exact request; it may not claim that the care + team received, read, reviewed, or will answer it. +6. **Transitions are cross-surface.** Transfer, discharge, identity correction, + representative expiry/revocation, sensitive denial, and retraction must affect every + applicable surface, cache, handle, draft, and capability coherently. +7. **Accessibility is information equivalence.** Images, color, sound, gestures, reading + level, or one presentation mode may never become the only way to obtain essential + information or perform an allowed action. +8. **The catalog remains held.** It defines testable safety expectations but does not + select clinical sources, approve copy, define a live API, or authorize implementation. + +## 3. Evidence boundary + +### 3.1 Exact source inputs + +| Source | SHA-256 | Purpose | +| ----------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------ | ------------------------------------------------------------------------ | +| Empty Nightingale contract | `df7d74e6cde652bc2e8e25d74ec9c0238eeb00fb9e50403b25d32fc46bf425dd` | Proves zero executable paths and default-off activation | +| Encounter-access candidate | `6d3b587d80b5139e7ae64db1fb3f7a273f4991efd06ed13da0e2f7959ea2983b` | Opaque self-context and generic non-disclosure boundary | +| Encounter-access fixtures | `968373aba829b10259d8c40a107f11646a98ebd2f5d484ec695e3e424f7af157` | Current, absent, expired, revoked, and held relationship cases | +| Today candidate | `4f272e90fd306f878a1bcad45c8a46ff1c7aa2e743f3dfc99e53089fa1bef012` | Field governance, section states, correction, freshness, and no-store | +| Today fixtures | `9efdc028ec9a6cf547881d73b6e65c69d633daf63b09ec594d1a9b52f6b9d72e` | Synthetic success, withholding, stale, correction, and outage outcomes | +| Identity/session/recovery candidate | `8729d9cd7e3cc742678ef61768570349ab8c0fe9610bacd66c13ad5318dc5daa` | Held assurance, recovery, revocation, and identity-state model | +| Identity/session/recovery fixtures | `8fb4fdb5789b2a35225ff3a7fa02a26d3c33e155baa859ed8a6f21b319d456e7` | Synthetic identity transitions and default-deny outcomes | +| Current-inpatient source candidate | `214a18cfdff6661ba6da3c29dbde74ba4299999087c219da38004cd291c3cf67` | Authoritative-source, lifecycle, race, and freshness requirements | +| Current-inpatient source fixtures | `61acdc5b273090023eb7c077df0bf737bb20bef658d001fb4b6ef1a279cb7240` | Current, changed, closed, ambiguous, stale, and unavailable source cases | +| Communication/notification ledger | `a3a3beb667f7007ccd9dd8abbb15acf0beb9248adc48cd0a704912baafbedf6e` | Acceptance, replay, routing, handoff, delivery, urgency, and outage gaps | +| Journey/preference/presentation/release ledger | `b24612869036dd1ee208dd1629a35e58bb106433c59f51c5bf31b3856485ff0f` | Full predecessor source classification and held/rejected dispositions | +| [Original Hummingbird parity plan](../hummingbird/ZEPHYRUS-HUMMINGBIRD-FUNCTIONAL-PARITY-AND-PATIENT-EXPERIENCE-PLAN-2026-07-19.md) | `5b3f15dbc944e9890070cf3eae3fca31b0a4e68b84d0174e09497e55d3276246` | Required patient reference-scenario list and unresolved approval gates | + +### 3.2 What the evidence can and cannot prove + +The source material can prove that the repository contains specific candidate semantics, +legacy behaviors, gaps, tests, and governance controls. It cannot prove: + +- that a clinical source is authoritative for Nightingale; +- that a patient-facing term, timing statement, result, or instruction is approved; +- that a representative relationship is legally effective; +- that a staff pool, handoff, interpreter, notification provider, or support workflow is + operational; +- that a synthetic sequence matches a production integration; +- that a native screen is understandable or accessible to patients; or +- that any residual security or clinical risk has been accepted. + +No production database, deployed application, identity provider, EHR, device provider, +patient record, staff record, source response, message body, or real identifier was used. + +## 4. Reference-scenario inventory + +| Family | Cases | Minimum invariant | +| --------------------------------- | -----: | -------------------------------------------------------------------------------------------------------------------- | +| Admission | 1 | A current inpatient context does not imply that clinical content is released or that an empty section means no care. | +| Intra-hospital unit transfer | 1 | Withdraw old location before showing a separately released replacement; never guess new routing ownership. | +| Procedure during admission | 1 | Show only separately released state/timing; never infer start, completion, result, or outcome from the clock. | +| Ancillary test and result | 3 | Delay is a released correction; source result existence is withheld until an independent patient release. | +| Pre-round question | 2 | Exact server acceptance is distinct from staff projection, delivery, review, response, and notification. | +| Shift handoff | 1 | Preserve thread/message identity and content-free accountability; unresolved is safer than a guessed owner. | +| Changed discharge estimate | 1 | Atomically replace the old estimate and retain uncertainty; an estimate is never a promise. | +| Discharge with open thread | 1 | Revoke inpatient compose before another mutation, reconcile the thread, and purge the draft/handle. | +| Identity merge/correction | 1 | Revoke old sessions and handles; never auto-union records; require approved reproof and linkage. | +| Representative lifecycle | 4 | Invitation is not access; future access is scoped; expiry and revocation purge capability, cache, handle, and draft. | +| Language and interpreter | 1 | Use only approved per-field language; interpreter status is separate; no unreviewed clinical machine translation. | +| Accommodation equivalence | 5 | Visual, hearing, motor, cognitive, and low-literacy modes retain essential information and permitted actions. | +| Sensitive-data denial | 1 | Withhold every surface and existence signal when sensitive authorization is absent, ambiguous, or denied. | +| Source outage and staleness | 2 | Outage is not released-empty; stale display requires a field-specific rule, observed time, and notice. | +| Content retraction and correction | 2 | Retraction removes the value everywhere; correction uses a separately reviewed replacement with complete context. | +| **Total** | **27** | Every case is synthetic, held, no-store, release-gated, audited without content, and rollback-aware. | + +## 5. Detailed scenario decisions + +### 5.1 Uncomplicated adult admission + +The synthetic admission case starts only after a separately held identity/session flow and +an authoritative current-inpatient source establish one self context. It deliberately +starts with no released patient projection. + +Expected behavior: + +- encounter discovery may move from zero to one opaque context; +- Today, My Path, and Care Team remain independently not available until their content is + released; +- a section may be `released-empty` only after a complete governed evaluation; +- the UI must explain that no released items does not mean no care is planned; and +- no diagnosis, procedure, clinician, location, or discharge fact is inferred from + admission context alone. + +### 5.2 Intra-hospital unit transfer + +A transfer can invalidate location, care-team presentation, communication capability, and +accountable routing at different times. The candidate therefore rejects a client-only +location swap or silent retention of the old unit. + +Required sequence: + +1. reconcile the authoritative transfer and current-context state; +2. withdraw the old patient-facing location before disclosing a replacement; +3. release the new location only after its own source/release decision; +4. revalidate communication capability against the current context; +5. append a content-free reroute fact only when an eligible destination resolves; and +6. use `unresolved` instead of guessing a pool or staff owner. + +The patient must not be told that a particular person received or read a message merely +because responsibility routing changed. + +### 5.3 Procedure during admission + +The procedure case allows only separately released plan, timing-confidence, uncertainty, +and can-change fields. It prohibits: + +- treating scheduled time as actual start; +- treating elapsed time as in-progress or completed state; +- treating completion as a result or outcome; +- creating patient language from an internal code; or +- copying unreviewed perioperative or clinical prose into the patient product. + +Procedure vocabulary, timing semantics, source ownership, and content still require named +clinical approval. + +### 5.4 Ancillary test delay and result release + +Three cases separate concerns that legacy surfaces can collapse: + +1. **Released delay:** withdraw prior timing, then show only an approved delayed state, + uncertainty, and optional updated window. Do not disclose operational blame or infer + cancellation. +2. **Unreleased result:** a source fact may exist, but no patient-facing existence, status, + value, interpretation, or review state is exposed. +3. **Separately released result:** an independently approved result field may appear with + complete governance context. Its presence still does not imply clinical interpretation + or a completed care-team discussion. + +### 5.5 Pre-round question and approved response + +The two communication cases encode the distinction missing from patient-visible delivery +wording: + +- an exact idempotent submission can become `server accepted`; +- that state means durable server acceptance only; +- a content-free accountable routing fact is separate; +- staff projection, individual receipt, review, and response are not inferred; +- the draft clears only after the exact accepted response, not after an ambiguous result; +- a staff response is a separate immutable message and must be approved for patient + release; and +- patient push remains held until provider, token, payload, consent, lifecycle, and native + capability designs are approved. + +Routine messaging must retain an unmistakable urgent-help escape path, but this candidate +does not approve urgent wording or monitoring claims. + +### 5.6 Shift handoff + +The handoff case preserves thread and message identity while accountability changes through +append-only, content-free routing facts. It requires a fresh authoritative workforce input, +effective eligibility, and a resolvable destination. If no destination is eligible, the +state is `unresolved`; neither server nor client guesses. + +The patient may receive only separately approved generic status. No internal pool, +staffing detail, staff name, guaranteed response time, or read claim is exposed. + +### 5.7 Changed estimated discharge date + +The old estimate is atomically withdrawn before the separately released replacement is +serialized. The replacement carries its own: + +- observation and release times; +- confidence and uncertainty; +- can-change flag; +- language release; +- correction state; and +- patient notice. + +The patient notice does not reveal an internal reason, actor, source identifier, or blame. +An estimate is never described as a promise. + +### 5.8 Discharge while a thread is open + +Authoritative discharge closes the inpatient access context before another patient +mutation can succeed. The future approved lifecycle policy must decide whether the thread +is closed, omitted, or handed off to a separate portal/support product. Until that policy +exists, Nightingale: + +- revokes compose capability; +- withholds inpatient surfaces generically; +- purges the context handle, sensitive cache, and volatile draft; +- rejects queued or replayed mutations; and +- makes no claim that the care team read or resolved the prior question. + +### 5.9 Patient identity merge or correction + +Identity correction is treated as an authorization discontinuity, not a background data +refresh. The old session family and context handles are revoked, device state is purged, +and no records are auto-unioned in the client. New access requires approved reproof and an +authoritative unambiguous link. + +The response does not expose whether another identity or record exists, the merge target, +the old or corrected content, or the reason for review. + +### 5.10 Representative invitation, scope, expiry, and revocation + +Representative access remains entirely held. The four cases exist to prevent a future +implementation from treating partial workflow state as authority: + +- invitation does not grant or reveal access; +- verification, relationship, authority/consent, operation scope, field scope, expiry, + sensitive-service exclusions, and communication authority are separate; +- self and representative realms never share handles, sessions, caches, or drafts; +- expiry and revocation deny every disclosure and mutation before serialization; and +- revoked/expired state is generically indistinguishable from other unavailable outcomes. + +The scoped case still discloses nothing because no representative model is approved. + +### 5.11 Limited-English-proficiency patient with interpreter + +Language preference, patient-visible content language, and interpreter workflow are +separate facts: + +- each displayed field must have an approved translation or approved source-language + release; +- absent translation is withheld or handled through an approved fallback, never + unreviewed clinical machine translation; +- interpreter requested, scheduled, confirmed, present, or unavailable states require a + separate authoritative source and released vocabulary; and +- selecting a language does not promise that an interpreter is confirmed. + +### 5.12 Accommodation equivalence + +Five cases specify minimum cross-surface equivalence: + +- **Visual:** reflow, screen-reader semantics, magnification/contrast, images-hidden + equivalence, and no color-only state. +- **Hearing:** no sound-only alert and approved captions/transcripts for any future media. +- **Motor:** supported alternative input, coherent focus, adequate targets, and no + gesture-only action. +- **Cognitive:** consistent structure, restrained status changes, explicit uncertainty, + one primary purpose per step, and a distinct urgent-help path. +- **Low literacy:** approved plain language that retains material meaning, plus teach-back + semantics that never equate opening/completing a screen with understanding. + +These cases are requirements for future validation, not claims that the current offline +shell or any future journey has passed full accessibility review. + +### 5.13 Sensitive-data case + +When an effective sensitive-service policy is absent, ambiguous, or denies access, every +field, section, thread, action, cached value, draft, and capability fails closed. The +patient or representative receives no signal that the resource or sensitive service +exists, what type it is, or why it was restricted. + +This candidate does not decide which services are sensitive or what self/representative +rights apply. Those are legal, privacy, clinical, identity, and product decisions. + +### 5.14 Source outage and stale projection + +The outage case distinguishes incomplete evaluation from an approved empty section: + +- affected sections become `not-available`, never `released-empty`; +- independently governed unaffected fields may remain; +- the last known value is not silently shown as current; and +- the outage does not imply cancellation, discharge, transfer, or no care. + +The stale case is field-specific. A value may be shown stale only if an approved policy +permits it and supplies an observation time plus patient notice. Otherwise it is withheld. +One document-level freshness value cannot override field state. + +### 5.15 Incorrect content retracted and corrected + +Retraction and correction are separate cases: + +- retraction atomically removes the value from every surface and purges any approved cache; +- no withdrawn value, internal target, actor, source, or reason is disclosed; +- a replacement appears only after independent review and release; +- the replacement carries its own complete governance context; and +- a source-free patient correction notice may be shown only after copy approval. + +## 6. Cross-cutting contract and platform requirements + +### 6.1 Authorization sequence + +Every eventual operation must apply an operation-specific version of this sequence: + +```text +active Nightingale contract operation + -> product / facility / cohort / expiry gates + -> Nightingale realm, principal, assurance, and current session + -> verified self or separately approved representative authority + -> opaque context ownership + -> authoritative current-inpatient state + -> operation and field capability + -> sensitive-service and relationship rules + -> release, freshness, uncertainty, language, and correction + -> race recheck before serialization or mutation + -> durable content-free audit + -> atomic no-store response or exact idempotent mutation result +``` + +Client navigation, cached capability, possession of a handle, prior disclosure, hidden +controls, or a response from legacy Hummingbird is never authority. + +### 6.2 Non-disclosure + +Every fixture requires omission of: + +- source-system, patient, principal, grant, encounter, staff, and routing identifiers; +- unreleased clinical values; +- withheld resource existence and denial reason; +- internal pool/staffing detail; +- approval actors and correction targets; and +- raw patient-authored text from audit, routing, monitoring, and fixture metadata. + +### 6.3 Communication and notification + +The candidate permits no communication mutation or notification delivery. The scenarios +define future state meaning only: + +- accepted means accepted by the server; +- staff projection, delivery, reading, review, and response remain distinct; +- exact retries must reuse operation and client-message identity; +- changed retries conflict instead of duplicating; +- routing metadata remains content-free; +- no eligible owner is `unresolved`; +- notification registration is not delivery; and +- no notification payload, provider, permission, background delivery, or deep link exists + in Nightingale. + +### 6.4 Offline, cache, and draft behavior + +Every case denies offline PHI, queued mutation, and presentation of a stale value as +current. A future last-known-value feature needs a separate field policy and threat review. +Context loss, identity correction, representative expiry/revocation, sensitive denial, +discharge, and retraction require handle/cache/draft purge. + +### 6.5 Audit and rollback + +The catalog requires content-free evaluation, authorization, field-key disclosure, +communication-state, and withhold/correction/outage facts. It forbids raw patient values, +message bodies, and internal identifiers in those facts. + +Every case requires a kill switch, release withdrawal, and relevant handle/cache/draft +purge. These requirements do not prove that monitoring, incident response, or rollback is +implemented. + +## 7. Mechanical verification + +The verifier fails if: + +- the executable foundation gains a path or activation flag; +- any of 19 global runtime/production permissions becomes true; +- any family or case is missing, reordered, duplicated, or reassigned; +- a fixture stops being synthetic and held; +- a required approval, separate-release rule, atomic response, or no-store rule is + removed; +- a representative or sensitive-data case exposes a surface; +- a concrete UUID, email, API path, patient/staff/source identifier key, message body, or + clinical value enters the fixtures; +- a notification state implies delivery or server acceptance implies staff delivery/read; +- offline PHI, a queued mutation, current presentation of stale data, essential imagery, + color-only state, or action loss becomes permitted; +- identity correction no longer requires reproof; +- source outage can present last known data as current; +- retraction stops removing content atomically; +- source files or fixture serialization drift; or +- either artifact no longer matches the deterministic builder; or +- any of 19 required findings is softened. + +The 23 negative self-tests deliberately introduce those failures and require each one to +be rejected. + +## 8. Remaining approval and implementation gates + +This scenario catalog can support review, but it cannot satisfy the reviews it calls for. +Before any live journey implementation: + +1. approve identity, recovery, current-inpatient source, lifecycle, race, and + representative designs; +2. approve each operation and its authorization/non-disclosure matrix; +3. approve each field's source, release authority, freshness, uncertainty, correction, + translation, and offline rule; +4. approve patient state/timing vocabulary and all patient-visible copy; +5. approve communication replay, routing, urgency, response, and lifecycle semantics; +6. approve notification provider, payload, consent, permission, revocation, and support + behavior; +7. approve legal/privacy/clinical sensitive-service and representative policy; +8. complete patient-advisor, accessibility, language/interpreter, and accommodation + review; +9. implement canonical backend/iOS/Android fixtures in default-off non-production + integration; +10. run cross-surface transition, outage, security, clinical-hazard, recovery, and rollback + tests; and +11. obtain a separate pilot go/no-go decision. + +## 9. Explicit non-claims + +Completion of this candidate means only that the listed synthetic scenarios have a +machine-enforced, internally consistent, default-deny reference outcome. It does not mean: + +- Today, My Path, Care Team, education, discharge, communication, or notification exists; +- any clinical content, term, timing statement, source, or patient instruction is correct; +- representative or sensitive-service access is legally or clinically approved; +- production integration, reliability, security, accessibility, or patient comprehension + has been proven; +- a sample or real patient was created or accessed; +- a distribution artifact, store record, pilot, migration, or deployment is authorized; + or +- the overall Nightingale product is clinically ratified or complete. diff --git a/docs/nightingale/PATIENT-STATE-VOCABULARY-CLASSIFICATION-2026-07-26.md b/docs/nightingale/PATIENT-STATE-VOCABULARY-CLASSIFICATION-2026-07-26.md new file mode 100644 index 00000000..8d5d68f7 --- /dev/null +++ b/docs/nightingale/PATIENT-STATE-VOCABULARY-CLASSIFICATION-2026-07-26.md @@ -0,0 +1,176 @@ +# Nightingale patient-state vocabulary classification + +**Status:** Source-level migration analysis only. No state code, label, locale, schema, +projection, client behavior, or patient-facing copy is approved for Nightingale. + +**Decision date:** 2026-07-26 + +**Related records:** + +- [Contract ownership and authorization matrix](./CONTRACT-OWNERSHIP-AND-AUTHORIZATION-MATRIX-2026-07-26.md) +- [Migration classification](./MIGRATION-CLASSIFICATION-2026-07-26.md) +- [Empty/default-off contract foundation](./api-contract/nightingale-foundation.v0.json) + +## 1. Outcome + +The Hummingbird Patient state vocabulary is useful evidence, but it is not a coherent, +portable Nightingale vocabulary. The reviewed sources disagree about domain coverage, +schema placement, decoding behavior, and what an absent vocabulary version means. +Nightingale therefore adopts only the following design principles at this stage: + +1. wire codes and patient-facing labels are different governed artifacts; +2. internal codes must never be mechanically title-cased or shown to a patient; +3. every patient-visible code requires an exact domain, locale, version, and reviewed label; +4. an absent, unknown, malformed, or incompatible version fails closed for the affected + projection; +5. unknown codes and unrecognized enum values withhold the affected field or projection + according to an approved field-safety matrix; and +6. backend, contract fixtures, iOS, and Android must be generated from or mechanically + reconciled to one Nightingale registry before the first patient projection is enabled. + +No vocabulary implementation is added to either Nightingale native app in this slice. + +## 2. Evidence snapshot + +The review used the source tree after merging `origin/main` commit `84b5f830`. + +| Source | SHA-256 | Observed role | +| ----------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------ | ------------------------------------------------------------------------ | +| `config/hummingbird-patient-content.php` | `a4f23f9efba3e8364953cfa6bc2e45183ae8f054fdd49b012f684cd86e80aefa` | Backend English registry: 12 domains, 49 code-label pairs, draft version | +| `app/Services/Patient/Projection/PatientProjectionStateVocabulary.php` | `6c0d8fc2b5e268612ff043c192c9d0ded06cb211af9a2e559c7a25f2db953f01` | Backend domain/code/label validation | +| `docs/hummingbird/api-contract/hummingbird-patient.v1.yaml` | `fb6220b4ef8eb106223624a9785256fdc1603995f281f430a79897905cb45a1b` | Legacy API schema and response metadata | +| `hummingbird/iosPatientApp/HummingbirdPatient/Models/PatientStateVocabulary.swift` | `e13015b0f09f58f845d64d202839d16be1ea36744b35445d95e6ef6904da231a` | iOS registry: 8 domains, 37 code-label pairs | +| `hummingbird/iosPatientApp/HummingbirdPatient/Networking/PatientAPIModels.swift` | `93c3250b7292c9abe17e3bb932e39c489332c8becc321663b4b8365a1e468759` | iOS response/version models plus a separate event-category enum | +| `hummingbird/androidPatientApp/app/src/main/java/net/acumenus/hummingbird/patient/data/PatientStateVocabulary.kt` | `023157f23c8aa372f6ef10e1d07086603cb507d446e9a18798cb3fcbf953660e` | Android registry: 9 domains, 41 code-label pairs | + +These hashes establish the reviewed inputs; they do not certify the legacy behavior. + +## 3. Domain-by-domain reconciliation + +| Backend domain | Codes | iOS reference | Android reference | Classification and Nightingale decision | +| --------------------------- | ----: | ------------------------------------------------------------------------------------------------ | ---------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------- | +| `schedule_status` | 7 | Registry as `.schedule` | Registry as `SCHEDULE` | Candidate code set and labels; held for clinical/content/language review | +| `stage_status` | 5 | Registry as `.pathway` | Registry as `PATHWAY` | Candidate; native naming is not the wire-domain name and must not become the canonical registry key | +| `milestone_status` | 5 | Registry as `.milestone` | Registry as `MILESTONE` | Candidate; currently duplicates stage labels but remains a distinct semantic domain | +| `pathway_event_status` | 5 | Registry as `.pathwayEvent` | Registry as `PATHWAY_EVENT` | Candidate; held with the pathway-event schema discrepancy below | +| `pathway_event_category` | 4 | Separate `PatientPathwayEventCategory` enum and `patientLabel`, outside `PatientStateVocabulary` | Registry as `PATHWAY_EVENT_CATEGORY` | **Parity defect.** Same English labels exist, but ownership/version enforcement differs | +| `rounds_topic_status` | 3 | Registry as `.roundsTopic` | Registry as `ROUNDS_TOPIC` | Candidate; patient-language and correction semantics unapproved | +| `discharge_criteria_status` | 3 | Registry as `.dischargeCriterion` | Registry as `DISCHARGE_CRITERION` | High-scrutiny candidate; “Needs attention” cannot imply a diagnosis, urgency, or discharge decision | +| `goal_status` | 6 | Registry as `.goal` | Registry as `GOAL` | Candidate; patient/proxy/team authorship and cancellation semantics require review | +| `goal_author` | 3 | Separate string switch in `PatientPathView` | Separate string switch in `PatientSessionCoordinator` | **Parity/governance defect.** Labels are duplicated outside both versioned registries | +| `timing_confidence` | 3 | Registry as `.timingConfidence` | Registry as `TIMING_CONFIDENCE` | Candidate; must be interpreted with timestamp, freshness, and “can change” rules | +| `location_status` | 3 | Decoded as an unchecked `String`, but not rendered as a status | Today decoder/model omits `care_location` | **Functional gap.** Backend-valid states have no cross-platform rendering contract | +| `contact_route` | 2 | Multiple separate string switches with different sentences | Multiple separate string switches with different sentences | **Parity/safety defect.** Code-to-action copy is duplicated and context-dependent; urgent guidance requires operational approval | + +The backend registry contains 49 code-label pairs. The iOS versioned registry contains 37; +its separate event-category enum brings its implemented label count to 41 but does not +bring that domain under the advertised registry/version. Android's versioned registry +contains 41. Neither client keeps `goal_author`, `location_status`, or `contact_route` +inside its versioned registry. + +## 4. Schema and decoder discrepancies + +### 4.1 Category is attached to different objects + +The legacy OpenAPI contract defines optional `category` on `PatientScheduleItem`, but the +backend content guard does not allow a schedule category and neither native schedule model +decodes it. Conversely, the backend content guard and both native clients support optional +`category` on a pathway event, while the legacy OpenAPI `PatientPathwayEvent` schema does +not define that property. + +This is not an additive-detail issue. It means the schema cannot currently prove the shape +that backend and clients exchange. Nightingale must not copy either placement until an +approved projection-field matrix resolves whether category belongs on schedule items, +pathway events, both as separate concepts, or neither. + +### 4.2 Android omits two Today subdocuments + +The legacy contract and iOS model include `care_location` and `discharge_outlook` in Today. +The Android `PatientTodayContent` model/decoder omits both. As a result: + +- `location_status` has no Android Today representation; +- Today-level discharge timing/readiness context is not cross-platform equivalent; and +- a successful Android decode can silently discard contract-declared content. + +Nightingale requires fixture-level field-presence reconciliation, not merely successful +JSON parsing. + +### 4.3 Unknown event categories fail differently + +iOS decodes pathway-event category into a closed `Codable` enum. An unknown non-null value +causes decoding of the containing response to fail. Android decodes the category as a +nullable string and maps an unknown value to the generic label “Status being confirmed.” +Those are materially different failure scopes. Nightingale needs an explicit per-field +rule—reject envelope, withhold projection, omit field, or show reviewed neutral copy—with +the same result on backend, iOS, and Android. + +### 4.4 Version absence is treated as compatible + +Both legacy native registries return compatible when `state_vocabulary_version` is absent, +as well as when it exactly matches `patient-state-vocabulary.v1-draft`. That preserves +older-server behavior but does not prove which labels governed the response. Nightingale +will require an explicit supported version for any response containing governed codes. +Absence is incompatible, not an implicit legacy version. + +### 4.5 Backend validation does not bind the advertised version + +The backend class reads the default locale's definitions and validates domain/code/label +syntax, but it does not itself validate or retain the registry `version`. Version metadata +is emitted separately by `PatientResponseMetadata`. Nightingale needs one immutable +registry identity that binds version, locales, domains, codes, labels, fixture checksum, +and response metadata. + +## 5. Candidate properties worth reimplementing + +These are candidate safety properties, not approved source code: + +- stable lower-snake-case wire codes; +- explicit contextual labels instead of code-derived display text; +- backend rejection of unknown domains and codes before release; +- neutral handling that never exposes an internal code; +- response-level vocabulary version metadata; +- client withholding on an explicitly incompatible version; and +- tests that enumerate every domain/code/label tuple. + +The legacy exception strings, Hummingbird configuration key, draft version, English copy, +domain aliases, separate switch statements, and `nil`-means-compatible behavior are +rejected as Nightingale implementation inputs. + +## 6. Required Nightingale vocabulary artifact + +Before a vocabulary implementation enters Nightingale, one reviewed artifact must define: + +| Required field | Required rule | +| ---------------------- | ----------------------------------------------------------------------------------------------- | +| Registry identity | Nightingale-owned name, immutable semantic version, content checksum, effective state | +| Domain | Exact wire-domain name and clinical/product meaning; no native-only alias as authority | +| Code | Stable wire value, definition, source, deprecated/replacement state | +| Patient label | Locale, reading level, context, screen-reader rendering, prohibited interpretations | +| Unknown handling | Exact field/projection failure scope and approved neutral experience | +| Provenance | Clinical/content owner, source authority, review date, evidence link | +| Timing | Relationship to source-observed time, generated time, freshness, uncertainty, and changeability | +| Relationship | Whether self and each representative class may receive the field | +| Translation | Human translation workflow, locale fallback, withdrawal and correction behavior | +| Compatibility | Supported server/client ranges, additive/breaking rules, forced withdrawal behavior | +| Cross-platform fixture | Canonical input and exact backend/iOS/Android output or withholding result | + +Code generation may be used only after the reviewed registry is authoritative. Generated +backend validators and native lookup tables must contain a pinned registry version/checksum +and pass a cross-platform reconciliation check in CI. + +## 7. First-operation gates affected by this finding + +The empty Nightingale contract must remain empty until at least: + +- the category placement discrepancy is resolved in an approved field matrix; +- Today field coverage is made intentional and equivalent across platforms; +- `goal_author`, `location_status`, and `contact_route` ownership is centralized; +- absent-version and unknown-code failure scopes are approved; +- the first read operation has canonical positive, unknown-code, unknown-version, + missing-version, stale, corrected, retracted, and unauthorized fixtures; +- patient/advisor, clinical-content, accessibility, language/interpreter, privacy, and + operational owners approve applicable labels and fallbacks; and +- CI proves exact registry/fixture parity across backend, iOS, and Android. + +No production database, deployed response, patient record, session, grant, projection, +feature flag, route, client, or network access was used for this classification. diff --git a/docs/nightingale/PRESENTATION-PREFERENCES-FOUNDATION-DECISION-2026-07-26.md b/docs/nightingale/PRESENTATION-PREFERENCES-FOUNDATION-DECISION-2026-07-26.md new file mode 100644 index 00000000..97e10348 --- /dev/null +++ b/docs/nightingale/PRESENTATION-PREFERENCES-FOUNDATION-DECISION-2026-07-26.md @@ -0,0 +1,356 @@ +# Nightingale presentation-preferences foundation decision + +**Status:** implemented foundation subset; locally verified; independent human review and +distribution release remain held + +**Decision date:** 2026-07-26 + +**Applies to:** Nightingale iOS and Android application roots only + +**Does not authorize:** patient data access, account preferences, networking, identity, +clinical content, production fixtures, pilot enrollment, deployment, or release + +## 1. Decision + +Nightingale now owns two device-local display-comfort choices: + +1. **Reduce motion in Nightingale** +2. **Hide decorative imagery** + +These choices are deliberately separate from every future care-account, communication, +locale, consent, notification, and clinical preference. They modify presentation only. +They cannot alter care information, consent, routing, release state, clinical meaning, or +the availability of essential text and controls. + +The application follows the stronger of the patient’s Nightingale choice and the relevant +system accessibility setting. A patient can ask Nightingale to reduce motion or hide +imagery even when the operating system has not requested it. Nightingale cannot use its +local controls to weaken a stronger operating-system accessibility condition. + +This is a bounded foundation decision. It closes the previously identified semantically +inert Android reduced-motion control gap and establishes equivalent policy behavior on +iOS. It does not establish app-wide WCAG 2.2 AA conformance or accessibility approval. + +## 2. In-scope behavior + +| Concern | iOS behavior | Android behavior | Patient-safety rule | +| ---------------------------- | ------------------------------------------------------------------------------- | ------------------------------------------------------ | -------------------------------------------- | +| Local storage | `UserDefaults` under two full Nightingale keys | private `SharedPreferences` file with two keys | no care-account or clinical store | +| System reduced motion | SwiftUI `accessibilityReduceMotion` | animator-duration scale equal to zero | system request cannot be overridden | +| Patient reduced motion | Nightingale-local Boolean | Nightingale-local Boolean | removes governed decorative transitions | +| System contrast/transparency | increased contrast or reduced transparency hides imagery and makes cards opaque | Android 14+ positive system contrast hides imagery | stronger system condition wins | +| Accessibility text size | decorative art attenuated | decorative art attenuated at font scale 1.3 or greater | text remains foreground content | +| Patient imagery choice | hides foreground and scenic Nightingale art | hides foreground and scenic Nightingale art | essential text and controls remain | +| Persistence | survives app relaunch | survives activity recreation and cold relaunch | device-local presentation state only | +| Network/account sync | none | none | no API, account, or cloud preference binding | + +No patient record, identity, encounter, grant, account, clinical projection, message, or +server preference is read or written by this behavior. + +## 3. Namespace and storage boundary + +### 3.1 iOS + +The iOS application persists only: + +- `net.acumenus.nightingale.presentation.v1.reduce-motion` +- `net.acumenus.nightingale.presentation.v1.hide-decorative-imagery` + +The application uses `UserDefaults.standard`; it does not use +`NSUbiquitousKeyValueStore`, an account model, protected clinical storage, or a network +client for these settings. The UI-test reset environment variable is enclosed in +`#if DEBUG`, and the Release simulator binary scan proves the reset variable is not +present in that artifact. + +This implementation does not intentionally roam settings through Nightingale or a care +account. iOS operating-system backup or device-restore treatment of application defaults +has not been independently proven or excluded. Distribution evidence must therefore not +claim that an Apple-managed device restore can never carry these values. + +### 3.2 Android + +Android persists only these keys in the private +`net.acumenus.nightingale.presentation.v1` preferences file: + +- `reduce-motion` +- `hide-decorative-imagery` + +Each write uses a synchronous commit. The in-memory presentation snapshot changes only +after the private-store commit reports success. The application manifest sets +`android:allowBackup="false"`, and both the legacy full-backup rules and Android data +extraction rules exclude all shared preferences from cloud backup and device transfer. + +The Android production source contains no test-only clear or persisted-key inspection API. +Instrumentation inspects the private preference file through the activity only in the test +source set. + +### 3.3 Roaming decision + +Nightingale does not implement account, API, cloud-key-value, or cross-device synchronization +for these accessibility choices. Android backup and device transfer are explicitly +excluded. Apple-managed backup/restore behavior is an external release-evidence question, +not a Nightingale synchronization feature; that question remains open. + +Future product work must not silently move either local choice into an account preference. +Any proposed roaming behavior requires a new privacy/accessibility decision covering shared +devices, representatives, stale settings, conflict resolution, and patient control. + +## 4. Precedence truth table + +### 4.1 Reduced motion + +| System requests reduced motion | Patient requests reduced motion | Effective result | +| ------------------------------ | ------------------------------- | ------------------------------------ | +| no | no | governed gentle transitions may run | +| no | yes | governed transition duration is zero | +| yes | no | governed transition duration is zero | +| yes | yes | governed transition duration is zero | + +### 4.2 Decorative imagery + +| Patient hides imagery | Stronger system visual condition | Effective result | +| --------------------- | -------------------------------- | -------------------------------------------- | +| no | no | decorative Nightingale imagery may be shown | +| yes | no | all decorative Nightingale imagery is hidden | +| no | yes | all decorative Nightingale imagery is hidden | +| yes | yes | all decorative Nightingale imagery is hidden | + +On iOS, a stronger visual condition means increased contrast or reduced transparency. On +Android 14 and later, it means positive system contrast. At accessibility text sizes, +imagery is retained only when otherwise permitted and is attenuated behind content. + +## 5. Governed motion-site inventory + +The foundation application has no route transitions, clinical refresh animation, animated +charts, progress choreography, parallax, video, or patient-content motion. The governed +motion sites are therefore intentionally small and enumerable. + +| Motion site | iOS | Android | Reduced-motion result | +| ------------------------------------ | ------------------------------------- | --------------------------------------- | ---------------------------------------------------------------------------------- | +| privacy-cover appearance/removal | opacity transition and root animation | lifecycle cover changes synchronously | iOS uses identity/no implicit animation; Android has no animated cover transition | +| scenic-image alpha change | SwiftUI presentation-policy animation | `animateFloatAsState` with 180 ms tween | iOS disables the preference animation; Android uses `snap()` and zero milliseconds | +| local preference presentation update | 180 ms ease-in/out when permitted | image-alpha tween when permitted | transition duration becomes zero | + +Every future Nightingale animation must be added to this inventory or its successor and +must consume the same effective policy or an independently approved equivalent. A stored +or announced preference with no rendering effect is prohibited. + +## 6. Decorative imagery behavior + +The foreground brand mark and full-page scenic Nightingale artwork are decorative. They +have no accessibility label and never carry care information. + +When imagery is hidden: + +- the foreground bird mark is removed; +- the background bird image is fully transparent or not rendered; +- essential headings, privacy copy, status copy, toggles, and explanatory text remain; +- the base surface remains present; and +- the UI explicitly states that decorative imagery is hidden and essential content remains + available. + +When imagery is permitted at an accessibility text size, its opacity is reduced. When iOS +increased contrast or reduced transparency is active, imagery is withheld and card +backgrounds become opaque. Android’s high-contrast policy similarly withholds imagery and +uses opaque scrims. + +This foundation test does not prove every future Nightingale journey at the largest text +size, in landscape, or with language expansion. Those broader requirements remain open. + +## 7. Patient-facing semantics + +Both platforms present the section heading **Display comfort** and the same two controls. +The explanatory boundary states: + +> These settings are stored by Nightingale, not your care account. They never change your +> care information. + +Each control has a Nightingale-specific accessibility identifier/test tag, a patient-readable +label, a checkable state, and effect copy: + +- motion permitted: “Gentle transitions are enabled. Nightingale also follows your system + Reduce Motion setting.” +- motion reduced: “Motion is reduced. Nightingale changes views without decorative + movement.” +- imagery shown: “The Nightingale artwork is shown softly behind the page.” +- imagery hidden: “Decorative imagery is hidden. Essential text and controls remain + available.” + +Decorative images are excluded from the accessibility tree. The safe-shell and privacy-cover +semantics remain Nightingale-only. + +## 8. Implementation map + +| Responsibility | iOS | Android | +| --------------------------- | --------------------------------------------- | ---------------------------------------------- | +| keys, snapshot, persistence | `NightingalePresentationPreferences.swift` | `NightingalePresentationPreferences.kt` | +| system-signal collection | `NightingaleApp.swift` environment values | `MainActivity.kt` settings and `UiModeManager` | +| pure policy resolution | `NightingaleSceneAccessibilityPolicy.resolve` | `nightingaleSceneAccessibilityPolicy` | +| controls and effect copy | `NightingaleApp.swift` | `NightingaleVisualFoundation.kt` | +| scenic/cover consumption | `NightingaleVisualFoundation.swift` | `NightingaleVisualFoundation.kt` | +| unit tests | `NightingaleProductBoundaryTests.swift` | `NightingaleProductBoundaryTest.kt` | +| device UI tests | `NightingaleLaunchUITests.swift` | `NightingaleLaunchInstrumentedTest.kt` | +| compile-time boundary | `verify-nightingale-product-boundary.sh` | same repository verifier plus Gradle task | + +## 9. Automated proof + +### 9.1 Policy and persistence tests + +The iOS and Android unit suites independently prove: + +- no request produces gentle motion when the system requests reduced motion; +- a patient’s local reduced-motion request produces a zero-duration policy; +- hiding imagery produces zero image opacity; +- a stronger system contrast/transparency condition hides imagery; +- accessibility text size attenuates imagery when it remains permitted; +- preference namespaces are Nightingale-only; and +- no key includes a patient, account, token, or Hummingbird namespace. + +The device UI suites prove: + +- both controls are rendered and discoverable; +- effect text changes when each control is enabled; +- state survives relaunch/activity recreation; and +- the existing lifecycle privacy controls still pass. + +### 9.2 Product-boundary enforcement + +The repository verifier now fails if: + +- either presentation-policy source is missing; +- either iOS key or the Android namespace/key pair drifts; +- either implementation adopts an account/cloud-sync API; +- the iOS reset hook leaves a Debug conditional; or +- a production mobile source exposes test-only preference-clearing/inspection APIs. + +Existing boundaries continue to reject networking, legacy patient namespace, Android +`INTERNET`, or Nightingale activation. + +## 10. Native-device and build evidence + +### 10.1 iOS + +On iPhone 16e Simulator `3F568F29-BE58-49AD-8151-6C2303B4C4E3`, iOS 26.3.1: + +- `xcodebuild test` passed 10 of 10 tests with zero failures or skips; +- the new UI journey enabled both settings, verified changed effect copy, terminated the + application, relaunched it, and verified both switches remained enabled; +- the normally signed Release simulator build succeeded; +- visual inspection of the clean installed Release application confirmed the Nightingale + mark, calm scenic treatment, legible privacy boundary, and reachable display-comfort + controls; and +- the Release executable contained neither the Debug reset hook, the Hummingbird name, an + app network URL, nor `NSUbiquitousKeyValueStore`. The standard Apple property-list DTD + URL was identified and excluded from the application-endpoint assertion. + +Exact local artifact hashes: + +| Artifact | SHA-256 | +| -------------------------------- | ------------------------------------------------------------------ | +| iOS Debug simulator executable | `5f203f74c9b98f7700e00d52c05e0b676b8c458f07c385825cc59695709b3ca9` | +| iOS Release simulator executable | `663b1f9906e718dbd235512b1bc90cc9f9e47738a718bb71ac8cb686c6db0e81` | + +### 10.2 Android + +On the `hb` Android 15/API 35 AVD, cold-booted without a snapshot: + +- six JVM tests passed; +- six instrumentation tests passed; +- `verifyNightingaleProductBoundary`, `lintVitalRelease`, `assembleDebug`, and + `assembleRelease` passed; +- Gradle completed 117 actionable tasks, with 116 executed and one up-to-date; +- a manual accessibility-hierarchy interaction enabled both controls and a cold relaunch + retained both checked states and both enabled-state explanations; and +- the Release DEX contained no legacy Hummingbird name, test-reset API, Zephyrus endpoint, + API-path literal, or WebSocket endpoint, while the Release manifest declared no + `INTERNET` permission. Generic AndroidX/Compose diagnostic and schema URLs are library + strings and are not application endpoints. + +Exact local artifact hashes: + +| Artifact | SHA-256 | +| ---------------------------- | ------------------------------------------------------------------ | +| Android Debug APK | `ac851797682524dde8d739d9b6f4aa4eaaa0b77cc4e322b48e1ec21ec7149e1c` | +| Android unsigned Release APK | `9c23e30f1c7211f969ecb2cf68698591f74de409a3d689cf116c68e64fded3e1` | + +Android applies `FLAG_SECURE`. External emulator screenshots are therefore black by design. +That black capture is not treated as visual-layout proof. The passing Compose instrumentation +suite and the live UI Automator hierarchy establish rendered semantics and state; independent +human visual review on a physical or approved review device remains required. + +### 10.3 Evidence classification + +These hashes bind the checks to exact local Debug/Release artifacts from this implementation +slice. They are not distribution signatures, store artifacts, production builds, or +governance approval. Exact commit and CI evidence must be appended to the execution log +after publication. + +## 11. Failure history retained for reproducibility + +Initial Android preflight attempts failed before application verification because the shell +had neither a selected Java runtime nor Android SDK environment. Using the Android Studio +JDK and the local Android SDK exposed two compile defects: + +1. the proposed high-text-contrast API was not available at the selected compile surface; +2. the Compose delegated state required the `getValue` import. + +The implementation replaced the unavailable read with the Android 14+ +`UiModeManager.contrast` policy seam and added the required import. The complete clean task +set then passed. The earlier environment and compile failures are not counted as evidence. + +An attempted Release URL scan also found platform/library URLs. The scan was narrowed to +application endpoint and forbidden-boundary tokens while retaining the independent no- +`INTERNET` manifest assertion. The broader library-string result is not represented as an +application network finding. + +## 12. Residual risks and explicit holds + +The following remain open: + +- largest Dynamic Type/font-scale journeys on every future screen; +- complete reflow, focus-order, target-size, screen-reader, switch-control, contrast, + landscape, language-expansion, and right-to-left testing; +- iOS operating-system backup/restore behavior for local presentation defaults; +- physical-device and independent patient/advisor accessibility review; +- distribution signing, store artifact, and exact protected-`main` release evidence; +- automated enforcement that every future motion site consumes a governed policy; +- named privacy/security, clinical, content, language/interpreter, accessibility, + patient-advisor, support, and release approvals; and +- all identity, patient-data, Today, My Path, Care Team, communication, and production work. + +No automated test in this slice substitutes for those reviews. + +## 13. Rollback + +Rollback is source-local: + +1. remove the display-comfort card from each foundation screen; +2. remove the two platform preference stores and policy consumers; +3. remove only the two Nightingale presentation keys/preferences file during a separately + reviewed migration if data cleanup is required; and +4. retain the lifecycle privacy cover, Android secure-window control, no-network boundary, + and all other existing Nightingale safety primitives. + +A rollback must never clear care-account or protected clinical state. None is touched by +this implementation. + +## 14. Safety statement + +No production database or source was accessed. No patient, principal, identity link, grant, +session, encounter, projection, preference, message, pathway, release, feature flag, +migration, deployment, or pilot state was created, read, or changed. + +The implementation remains an offline, no-patient-data Nightingale foundation. + +## 15. Follow-up accessibility-layout evidence + +The later +[foundation accessibility and layout matrix](./FOUNDATION-ACCESSIBILITY-LAYOUT-MATRIX-2026-07-26.md) +found and corrected a dark-appearance iOS accent failure, added an explicit Android dark +scheme, corrected foundation semantic order, expanded complete interaction targets, and +proved the current shell at maximum text and landscape on both emulators. + +The original artifact hashes in this decision remain the historical display-comfort slice +and are not rewritten. The follow-up matrix records the replacement artifact hashes and +its expanded test counts. Full product conformance and independent human review remain +open. diff --git a/docs/nightingale/PRODUCT-IDENTITY-AND-SUPPORT-NAMING-CHECKLIST-2026-07-26.md b/docs/nightingale/PRODUCT-IDENTITY-AND-SUPPORT-NAMING-CHECKLIST-2026-07-26.md new file mode 100644 index 00000000..f76ae6f7 --- /dev/null +++ b/docs/nightingale/PRODUCT-IDENTITY-AND-SUPPORT-NAMING-CHECKLIST-2026-07-26.md @@ -0,0 +1,185 @@ +# Hummingbird and Nightingale Product Identity and Support Naming Checklist + +**Status:** Engineering identity registry and release-preparation checklist. No external +identifier, store record, signing entitlement, support endpoint, or distribution right is +claimed as reserved or approved by this document. + +**Initiative plan:** +[Nightingale Patient Product](../plans/nightingale-patient-product-2026-07-26.md) + +## 1. Canonical product registry + +| Surface | Hummingbird Staff | Nightingale | +| ---------------------- | ----------------------------- | ---------------------------------------------------------- | +| Audience | Hospital staff | Inpatients and permitted representatives | +| Display name | `Hummingbird` | `Nightingale` | +| Apple bundle ID | `net.acumenus.hummingbird` | `net.acumenus.nightingale` | +| Android application ID | `net.acumenus.hummingbird` | `net.acumenus.nightingale` | +| Current API boundary | Staff `/api/mobile/v1` | None in the foundation build | +| Support class | Workforce application support | Patient-facing support with clinical escalation boundaries | +| Product mark | Supplied hummingbird artwork | Supplied nightingale artwork | +| Release train | Hummingbird Staff | Independent Nightingale train | + +Forbidden release names include `Hummingbird Patient`, `HummingbirdPatient`, +`hummingbird.patient`, `Patient mode`, and any presentation that makes Nightingale look +like a role switch inside Hummingbird. Historic migration evidence may retain those terms +only when clearly labeled as reference lineage. + +## 2. External reservation and store records + +Every unchecked row requires an authorized owner to complete it in the named external +system and attach non-secret evidence. Repository work must never fabricate a reservation +or put credentials, signing keys, private support contacts, or store-session tokens into +source control. + +### Apple + +- [ ] Confirm the organization-controlled Apple Developer account and accountable owner. +- [ ] Verify or register the explicit App ID `net.acumenus.nightingale`. +- [ ] Create the Nightingale App Store Connect record with an organization-approved SKU. +- [ ] Establish distribution signing and certificate-rotation ownership. +- [ ] Decide, register, and review any keychain access group before protected storage exists. +- [ ] Decide whether an app group is necessary; default to none. +- [ ] Decide Universal Links and associated domains from an approved allowlist; default to none. +- [ ] Create a Nightingale-specific APNs topic and environment separation only after push + privacy review. +- [ ] Complete the privacy manifest and App Privacy disclosure from implemented behavior, + not planned behavior. +- [ ] Approve category, age rating, subtitle, keywords, copyright, export-compliance + answers, and review notes. +- [ ] Approve public support, privacy-policy, terms, and optional marketing URLs. +- [ ] Verify screenshots contain no patient information, stale product name, test hook, + internal hostname, or staff workflow. +- [ ] Record distribution-rights approval for the supplied nightingale artwork. + +### Google Play + +- [ ] Confirm the organization-controlled Play Console account and accountable owner. +- [ ] Verify or register `net.acumenus.nightingale` without repackaging a legacy patient app. +- [ ] Create the independent Nightingale Play record and controlled tester tracks. +- [ ] Establish Play App Signing, upload-key custody, rotation, and recovery ownership. +- [ ] Decide App Links from an approved host/path allowlist; default to none. +- [ ] Create an independent FCM project/topic boundary only after push privacy review. +- [ ] Complete Data safety, content rating, target-audience, ads, and account-deletion + declarations from implemented behavior. +- [ ] Approve title, short description, full description, category, release notes, and + store-listing localization. +- [ ] Approve public support email, support website, privacy-policy URL, and terms URL. +- [ ] Verify phone/tablet screenshots contain no patient information, stale product name, + test hook, internal hostname, or staff workflow. +- [ ] Record distribution-rights approval for the supplied nightingale artwork. + +### Hummingbird Staff reconciliation + +- [ ] Confirm the existing Apple and Google records are owned by the organization and use + `Hummingbird`, never `Hummingbird Patient`. +- [ ] Confirm signing, APNs/FCM, associated links, analytics, crash reporting, support + routing, and screenshots remain staff-only. +- [ ] Record distribution-rights approval for the supplied hummingbird artwork. +- [ ] Verify the icon replacement does not alter the existing Hummingbird application IDs + or silently create a new store listing. + +## 3. Support naming and routing + +Nightingale support must be patient-readable and operationally accountable before any +patient receives the app. Public contact values remain deliberately absent from this +repository until approved. + +- [ ] Name the accountable product-support owner, clinical escalation owner, privacy + incident owner, identity/recovery owner, accessibility owner, and after-hours owner. +- [ ] Approve a public support name that says `Nightingale Support`; do not route patients + to `Hummingbird Support` or an individual staff member. +- [ ] Approve support email, phone, hours, language/interpreter coverage, accessibility + accommodations, expected response time, and downtime wording. +- [ ] Define separate flows for technical help, account/identity recovery, privacy concern, + medical-record correction, care-information question, delayed message, and urgent + clinical need. +- [ ] Ensure every clinical or messaging support surface states that urgent needs use the + bedside call button or immediate staff assistance, not delayed app messaging. +- [ ] Ensure support tooling receives the minimum necessary information and never displays + raw clinical prose, staff notes, credentials, access tokens, or unrestricted logs. +- [ ] Define ticket retention, redaction, audit, correction, representative authority, and + cross-facility routing. +- [ ] Define incident and kill-switch messages under the Nightingale name with a reviewed + rollback owner and expiry. + +## 4. Namespace checklist + +Before a capability is introduced, its identifier must be independent and reviewed: + +- [ ] Keychain/encrypted-storage service and key names. +- [ ] Deep-link scheme, Universal Links, and Android App Links. +- [ ] Notification channels, categories, topics, and generic notification copy. +- [ ] Analytics and crash project, event names, user properties, and opt-out behavior. +- [ ] Support diagnostics and correlation identifiers. +- [ ] Accessibility identifiers and automated-test hooks. +- [ ] Localized string catalogs and store metadata. +- [ ] Build, artifact, SBOM, signing, and release-manifest names. +- [x] Reserve the repository-local foundation dependency-inventory identity as + `net.acumenus.nightingale.foundation-dependency-inventory` at schema version 1 and + the canonical path + `docs/nightingale/supply-chain/foundation-dependency-inventory.v0.json`. This closes + only the current inventory name; the combined build/artifact/SBOM/signing/ + release-manifest item above remains open. +- [ ] Privacy, terms, status, support, and account-deletion URLs. + +No Nightingale namespace may contain `hummingbird.patient`, and no Hummingbird namespace +may carry a Nightingale credential, patient grant, patient projection, or patient +communication. + +## 5. Engineering release scan + +The following checks are necessary but do not replace Apple, Google, legal, privacy, +security, clinical, accessibility, or patient-advisor approval: + +- [x] iOS and Android application IDs are distinct and compile. +- [x] User-facing foundation copy uses only the correct product name. +- [x] Hummingbird and Nightingale source artwork and generated output fingerprints are + distinct and pinned. +- [x] iOS AppIcon masters are RGB PNGs without alpha channels. +- [x] Android legacy, adaptive, round, and Android 13+ themed resources compile. +- [x] Actual iOS 26.3 and Android API 35 launcher/splash evidence shows the correct marks. +- [x] Nightingale’s compile-time boundary scan rejects Hummingbird identifiers and network + clients. +- [x] The generated, source-hash-bound foundation dependency inventory records seven direct + Android Release runtime declarations, 83 resolved components, 457 edges, zero iOS + third-party packages, and four Apple system-module imports; it expressly withholds + SBOM-conformance, vulnerability, provenance, license, signing, and approval claims. +- [x] Hummingbird's repository-owned WidgetKit, Live Activity, app-group, APNs, Android + widget, and notification-channel identities remain staff-only. +- [x] Nightingale has no unapproved notification, push, widget, extension, app-group, + shortcut, or Android receiver surface. +- [x] Repository scans find no native product-root store-listing package, and every + corresponding Hummingbird/Nightingale launcher, brand-mark, adaptive, round, and + monochrome asset remains distinct. +- [x] Source-predecessor Hummingbird builds install in place on clean iOS and Android + emulators, preserve a synthetic private-data canary, and retain exact application and + widget identities. This is not released-artifact or distribution-signing evidence. +- [ ] Retained released Hummingbird artifacts upgrade to approved release candidates with + monotonic version/build values, distribution-signing continuity, app-private and + protected-state continuity, installed-widget/Live Activity continuity, and verified + rollback behavior. +- [ ] Approved notification implementations render the correct product identity, generic + content, icon, actions, localization, redaction, and safe deep-link behavior on + supported devices. +- [ ] Store records, signing, public support endpoints, distribution rights, privacy + disclosures, and release approvals are complete. + +## 6. Approval record template + +For each external action, record: + +| Field | Required value | +| -------------------- | ------------------------------------------------------------- | +| Product and platform | Exact product plus Apple or Google | +| External record | Non-secret record ID or console URL | +| Accountable owner | Named organization role and person | +| Reviewer | Independent reviewer and review domain | +| Evidence date | ISO date | +| Approved values | Exact display name, ID, URL, and audience | +| Explicit exclusions | Names, capabilities, and data not authorized | +| Residual risks | Open risks accepted by the approval owner | +| Rollback/recovery | Revocation, key recovery, listing rollback, and contact owner | +| Expiry/re-review | Trigger or date requiring revalidation | + +An unchecked row is a hold, not an implicit approval. diff --git a/docs/nightingale/README.md b/docs/nightingale/README.md new file mode 100644 index 00000000..f1c71acf --- /dev/null +++ b/docs/nightingale/README.md @@ -0,0 +1,163 @@ +# Nightingale documentation + +Nightingale is the dedicated inpatient-facing Zephyrus product. This directory will hold +only its product-owned contract, safety evidence, release evidence, and migration lineage. + +## Current state + +The 2026-07-26 foundation has independent iOS and Android application identifiers and a +branded, patient-safe shell. It intentionally has no live patient access, network +permission, authentication, clinical projection, care-team messaging, or production +activation. The source of record is the +[Nightingale product plan](../plans/nightingale-patient-product-2026-07-26.md) and its +[execution log](../devlog/DEVLOG-nightingale-patient-product-2026-07-26.md). +The bounded 256-source Hummingbird Patient predecessor universe is fully classified across +three checksum-pinned ledgers; this is migration evidence, not implementation approval. A +held Today candidate now defines field-level release, freshness, uncertainty, language, +correction, and offline semantics across 68 synthetic outcomes while the executable +contract remains empty. A held cross-surface reference catalog now covers 15 patient-journey +families through 27 synthetic cases, including transfer, procedure, test/result release, +communication handoff, discharge, identity correction, representative lifecycle, language/ +interpreter support, accommodations, sensitive denial, source outage/staleness, and content +retraction/correction. The catalog has no operation or runtime permission and is not +clinical approval. A route-free generic non-disclosure foundation now collapses 39 of 40 +relationship/context/resource state combinations—including all required unknown, revoked, +expired, cross-principal, wrong-encounter, and omitted-resource cases—to one exact 404 +tuple; the sole positive combination may only proceed to later governed projection +evaluation. The gate is unreachable from an application request and adds no route, query, +content, or authorization. A separate route-free activation foundation now types clinical +approval, patient-content release, feature activation, pilot enrollment, and +source-connector deployment as five independent conjunctive gates. Thirty-one of 32 +combinations hold; the sole all-positive row may only continue to a future +operation-specific release evaluation. All five executable-contract flags remain false, +and the gate has no caller. A separate generated controlled-pilot candidate now defines +the required facility/unit/cohort/language/exclusion/support-hour scope, a 168-hour +validity ceiling, seven approval roles, append-only content-free audit events, and +rollback/kill-switch references. Its committed template is empty and inactive. Of 34 +synthetic cases, 33 hold and one may proceed only to an external go/no-go review; there is +no runtime evaluator, enrollment, route, deployment, or activation. The current native +namespace is also generated and +machine-verified across ten accessibility IDs, four Debug hooks, ten fully qualified +storage identifiers, zero telemetry events, and zero diagnostic channels, with no legacy +Hummingbird patient identity in runtime sources. Nightingale now also has two device-local display-comfort controls: +reduced motion and decorative-imagery suppression. They are presentation-only, remain +separate from care-account preferences, and do not authorize patient access. The current +offline shell now also has a bounded maximum-text, landscape, semantic-order, target-size, +and light/dark contrast matrix; this does not establish product-wide WCAG conformance or +human approval. A draft foundation threat and clinical-hazard model now versions the +current trust boundaries, implemented controls, 22 security/privacy threats, 22 clinical +hazards, 20 activation gates, incident requirements, and open risks. It is not a safety +case, compliance claim, residual-risk acceptance, or live-use approval. A generated +foundation dependency inventory now binds seven direct Android Release runtime +declarations, 83 resolved components, 457 dependency edges, and the current zero-package +iOS application target to exact source hashes. It is not a standards-conformant SBOM, +vulnerability/provenance assessment, or supply-chain approval. +The offline shell also consumes the same governed catalog of seven Nightingale background +photos on both platforms. Each local Gregorian day maps to the same catalog entry on iOS +and Android; the images are decorative, suppressible, hidden from accessibility meaning, +and packaged behind strong scrims and content surfaces. Source lineage and exact derivative +hashes are retained. A separate per-asset rights review now binds the seven source records, +corroborates two Unsplash asset pages and one byte-for-byte current provider download, +leaves five exact sources unresolved, and records zero durable archives, zero cleared +assets, and zero distribution-eligible assets. External distribution remains blocked +until complete durable source/terms evidence and named release-owner review are recorded. +The current offline shell now also has a 15-string, exact cross-platform English source +contract, three ordered headings, restrained status announcements, and native Debug +language-readiness journeys. iOS passes rendered double-length and right-to-left layout +tests; Android passes `en-XA` expansion and `ar-XB` mirroring. Release artifacts exclude +all pseudolocales. These are bounded engineering checks, not approved translations, human +VoiceOver/TalkBack review, or full accessibility conformance. +The offline foundation also now packages an exact iOS privacy manifest and an explicit +Android network-security policy. Native and Release-artifact checks prove no tracking or +collected-data declaration in the current offline iOS bundle, app-local `UserDefaults` +reason `CA92.1`, no Android network permission, cleartext denial, system-only trust, and +backup exclusion. This is candidate engineering evidence, not a store declaration, +privacy approval, or statement about a future connected product. + +An explicitly authorized production operation on 2026-07-27 cloned only the safe +Hummingbird reference-patient shape into one Nightingale synthetic encounter and one +pending/inactive, contactless/passwordless Nightingale principal. The source record remains +unchanged, and the Nightingale clone has no identity link, grant, challenge, session, +projection, route, or native caller. See the +[production sample-patient evidence](../evidence/nightingale/production-sample-patient-2026-07-27/README.md). +Current-main reconciliation subsequently preserved the independent Nightingale roots and +restored the deprecated Hummingbird Patient sources after an incompatible upstream rename. +The exact tree-equivalence and full native reacceptance record is in the +[current-main reconciliation evidence](../evidence/nightingale/current-main-reconciliation-2026-07-27/README.md). + +Current product-identity records: + +- [Product identity and support naming checklist](./PRODUCT-IDENTITY-AND-SUPPORT-NAMING-CHECKLIST-2026-07-26.md) +- [Nightingale TestFlight and iOS distribution boundary](./TESTFLIGHT.md) +- [Legacy migration classification](./MIGRATION-CLASSIFICATION-2026-07-26.md) +- [Identity, recovery, and protected-state decisions](./IDENTITY-RECOVERY-AND-PROTECTED-STATE-DECISIONS-2026-07-26.md) +- [Contract ownership and authorization matrix](./CONTRACT-OWNERSHIP-AND-AUTHORIZATION-MATRIX-2026-07-26.md) +- [Patient-state vocabulary classification](./PATIENT-STATE-VOCABULARY-CLASSIFICATION-2026-07-26.md) +- [Encounter-access held-candidate decision](./ENCOUNTER-ACCESS-CANDIDATE-DECISION-2026-07-26.md) +- [Today projection held-candidate decision](./TODAY-PROJECTION-CANDIDATE-DECISION-2026-07-26.md) +- [Patient-journey reference-scenario held-candidate decision](./PATIENT-JOURNEY-REFERENCE-SCENARIO-CANDIDATE-DECISION-2026-07-27.md) +- [Generic non-disclosure route-free foundation](./GENERIC-NON-DISCLOSURE-FOUNDATION-2026-07-27.md) +- [Namespace and activation-separation foundation](./NAMESPACE-AND-ACTIVATION-SEPARATION-FOUNDATION-2026-07-27.md) +- [Controlled-pilot manifest foundation](./CONTROLLED-PILOT-MANIFEST-FOUNDATION-2026-07-27.md) +- [Generated foundation namespace manifest](./namespace/foundation-namespace.v1.json) +- [Generated controlled-pilot candidate](./pilot/candidates/v0/candidate.json) +- [Controlled-pilot synthetic fixtures](./pilot/candidates/v0/fixtures.json) +- [Route, compatibility, identity, and inpatient-source ADR](./ROUTE-COMPATIBILITY-IDENTITY-SOURCE-ADR-2026-07-26.md) +- [Identity, session, recovery, and inpatient-source held-candidate decision](./IDENTITY-SESSION-RECOVERY-AND-SOURCE-CANDIDATE-DECISION-2026-07-26.md) +- [Identity-input, enrollment/recovery, first-read, and error source classification](./IDENTITY-INPUT-FIRST-READ-ERROR-SOURCE-CLASSIFICATION-2026-07-26.md) +- [Communication and notification source classification](./COMMUNICATION-AND-NOTIFICATION-SOURCE-CLASSIFICATION-2026-07-26.md) +- [Journey, preference, presentation, synthetic, and release source classification](./JOURNEY-PREFERENCE-PRESENTATION-RELEASE-SOURCE-CLASSIFICATION-2026-07-26.md) +- [Presentation-preferences foundation decision and evidence](./PRESENTATION-PREFERENCES-FOUNDATION-DECISION-2026-07-26.md) +- [Foundation accessibility and layout matrix](./FOUNDATION-ACCESSIBILITY-LAYOUT-MATRIX-2026-07-26.md) +- [Foundation accessibility and language-readiness evidence](./FOUNDATION-ACCESSIBILITY-LANGUAGE-READINESS-2026-07-27.md) +- [Draft foundation threat and clinical-hazard model](./FOUNDATION-THREAT-AND-HAZARD-MODEL-2026-07-26.md) +- [Foundation dependency inventory decision and evidence](./FOUNDATION-DEPENDENCY-INVENTORY-2026-07-26.md) +- [Generated foundation dependency inventory](./supply-chain/foundation-dependency-inventory.v0.json) +- [Background asset governance and native integration](./BACKGROUND-ASSET-GOVERNANCE-AND-NATIVE-INTEGRATION-2026-07-26.md) +- [Background rights and source-archive review](./BACKGROUND-RIGHTS-AND-SOURCE-ARCHIVE-REVIEW-2026-07-27.md) +- [Foundation privacy and release-control evidence](./FOUNDATION-PRIVACY-RELEASE-CONTROLS-2026-07-26.md) +- [Governed background manifest](../../nightingale/backgrounds/backgrounds.v1.json) +- [Fail-closed background rights review](../../nightingale/backgrounds/rights/rights-review.v0.json) +- [Background catalog runtime and distribution rules](../../nightingale/backgrounds/README.md) +- [Empty/default-off contract foundation](./api-contract/nightingale-foundation.v0.json) +- [Identity candidate and fixtures](./identity/candidates/v0/candidate.json) +- [Current-inpatient source candidate and fixtures](./source-candidates/current-inpatient/v0/candidate.json) +- [Today projection candidate and fixtures](./api-contract/candidates/today/v0/candidate.json) +- [Patient-journey reference candidate and fixtures](./api-contract/candidates/patient-journeys/v0/candidate.json) +- [65-file checksum-pinned source ledger](./migration/candidates/v0/source-classification.json) +- [130-file communication/notification checksum ledger](./migration/candidates/v0/communication-notification-source-classification.json) +- [134-file journey/preference/presentation/release checksum ledger](./migration/candidates/v0/journey-preference-presentation-release-source-classification.json) +- [Launcher, themed-icon, and splash evidence](../evidence/nightingale/brand-identity-2026-07-26/README.md) +- [Accessibility/language-readiness screenshots and hierarchies](../evidence/nightingale/accessibility-language-readiness-2026-07-27/README.md) +- [Authorized production sample-patient evidence](../evidence/nightingale/production-sample-patient-2026-07-27/README.md) +- [Current-main product-boundary reconciliation evidence](../evidence/nightingale/current-main-reconciliation-2026-07-27/README.md) +- [Controlled-pilot manifest acceptance evidence](../evidence/nightingale/controlled-pilot-manifest-2026-07-27/README.md) +- [Background rights-review acceptance evidence](../evidence/nightingale/background-rights-review-2026-07-27/README.md) + +## Lineage and filing rules + +- The prior Hummingbird Patient native targets and patient contract remain immutable, + governed migration inputs. They are neither renamed nor shipped as Nightingale. +- The `0.0.0-governance` contract under `api-contract/` is deliberately empty and + non-routable. An operation may be added only after its owner, compatibility decision, + authorization/non-disclosure matrix, fixtures, and review evidence are defined. +- Candidate artifacts under `api-contract/candidates/`, `identity/candidates/`, + `source-candidates/`, and `migration/candidates/` are non-runnable decision/fixture + evidence. They do not add a path, bind a provider or source adapter, permit client + generation or a source query, or authorize implementation. +- The patient-journey reference catalog is a synthetic cross-surface acceptance model. Its + 15 families and 27 cases do not approve clinical content, representative access, + communication, notification, production data, or a live journey operation. +- The generic non-disclosure gate is a route-free domain invariant. Its one/39 truth table + does not approve a route, identity/source adapter, patient record, clinical release, + audit implementation, HTTP timing policy, native client, pilot, or production use. +- The three migration ledgers now mechanically cover all 256 tracked sources in the bounded + legacy Hummingbird Patient product universe. Complete classification means every source + has an evidence disposition; it does not mean any source is approved to migrate. +- Add clinical, content, privacy, accessibility, and release evidence under `safety/` only + with a dated reviewer/approval record. Draft evidence must say that it is draft. +- Never store patient information, credentials, tokens, production fixtures, or private + contact information in this directory. +- Background photos are decorative product assets, not clinical content or patient-state + signals. A catalog change requires an exact manifest/version update, native parity + verification, accessibility review, and a release-rights decision. diff --git a/docs/nightingale/ROUTE-COMPATIBILITY-IDENTITY-SOURCE-ADR-2026-07-26.md b/docs/nightingale/ROUTE-COMPATIBILITY-IDENTITY-SOURCE-ADR-2026-07-26.md new file mode 100644 index 00000000..1b29a8fa --- /dev/null +++ b/docs/nightingale/ROUTE-COMPATIBILITY-IDENTITY-SOURCE-ADR-2026-07-26.md @@ -0,0 +1,404 @@ +# Nightingale route, compatibility, identity, and inpatient-source ADR + +**Decision date:** 2026-07-26 + +**Status:** Adopted for namespace reservation and default-deny foundation only + +**Runtime status:** No route, identity provider, source adapter, disclosure, native client, or +production query is enabled + +**Related plan:** +[Nightingale Patient Product](../plans/nightingale-patient-product-2026-07-26.md) + +**Related candidate:** +[Encounter-access held candidate](./ENCOUNTER-ACCESS-CANDIDATE-DECISION-2026-07-26.md) +and [Today projection held candidate](./TODAY-PROJECTION-CANDIDATE-DECISION-2026-07-26.md) + +## 1. Decision summary + +Nightingale reserves `/api/nightingale/v1` as its independent backend namespace. Its first +held read-only candidate is named `GET /inpatient-contexts` with operation ID +`listNightingaleInpatientContexts`. + +Reservation prevents future collision and makes product ownership reviewable. It does **not** +register a Laravel route, add an OpenAPI path, permit client generation, configure a server, +enable identity, query a source, disclose a handle, or authorize deployment. + +Nightingale will not alias, proxy, redirect, mount, or silently rename the legacy +`/api/patient/v1` Hummingbird Patient surface. It will not use the staff +`/api/mobile/v1` surface. Legacy patient routes remain compatibility evidence and an +unchanged reference surface. Any reusable backend behavior must sit behind an independently +reviewed Nightingale adapter and Nightingale-owned request, response, authorization, audit, +and release contracts. + +The foundation introduces two request-scoped ports: + +1. `NightingaleIdentityBoundary`, whose unconfigured implementation returns + `unavailable`; and +2. `NightingaleInpatientContextSource`, whose unconfigured implementation performs no query + and returns `unavailable`. + +Only `verified_self` plus `confirmed_current` may continue to the later governed evaluation. +That continuation is explicitly **not** authorization. Identity-link, relationship, grant, +purpose, facility/cohort, cardinality, race, handle, durable-audit, response, and disclosure +gates still must pass before an operation could return data. + +## 2. Scope and non-decisions + +This ADR decides: + +- the reserved Nightingale API namespace; +- the held candidate path and operation name; +- the absence of HTTP compatibility aliases; +- the separation of Nightingale identity from the legacy patient and staff realms; +- the shape and default state of the identity and source prerequisite ports; +- the fail-closed source-state vocabulary; and +- which changes remain prohibited in this foundation. + +This ADR does not decide or approve: + +- identity proofing, federation, assurance, enrollment, recovery, session duration, device + binding, step-up, representative access, or break-glass behavior; +- a principal, token, cookie, credential, claim, scope, session, or storage schema; +- a mapping between Nightingale and legacy patient principals, grants, encounter UUIDs, or + source identifiers; +- the authoritative production encounter connector, freshness interval, facility/unit + cohort, observation class, boarding rule, transfer/readmission rule, or downtime policy; +- patient-visible encounter content or language; +- an OpenAPI operation, controller, middleware, service-container binding, route file, + database query, migration, client, app entitlement, or Android network permission; +- non-production or production activation; or +- clinical, privacy/security, accessibility, patient-advisor, legal/HIM, support, operations, + release, or deployment approval. + +## 3. Evidence baseline + +The source review was performed against branch `codex/nightingale-patient-product` after its +reconciliation with `origin/main` commit `84b5f830`. The exact reviewed files were: + +| Source | SHA-256 | Relevant observation | +| --------------------------------------------------------------------------------------- | ------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------ | +| `app/Providers/RouteServiceProvider.php` | `c387e94f57e8db13df318d1db3dbdd578fda3f9823538112f244e09ec9556490` | Laravel separately mounts staff/general API, care-pathway API, legacy patient API, and web routes. | +| `routes/patient.php` | `e892a9d570668bec6ae54d18cbfd891f7d9df2e2c2f8f6e3689125d6b465af28` | The legacy patient surface uses Hummingbird-specific flags, realm, abilities, throttles, controllers, and route names. | +| `bootstrap/app.php` | `fed17ddee14eed8dd650954193c7a276f6f41f5aecb946c5bfe264741688d56c` | Patient middleware aliases, exception decoration, and priority ordering are explicitly bound to the legacy patient route family. | +| `app/Models/Encounter.php` | `59e879ebe2780d093cb73328df30ad437519742aedabd0c02cb3a2cf22caa516` | The operational model maps to `prod.encounters`; its `active` scope checks status and deletion only. | +| `app/Services/Patient/PatientEncounterAccessService.php` | `a593503e20dc8ba45699af4974f36345a0e85b739fa2f428565c8d3d4b079ef9` | The legacy list service exposes grant-derived fields and filters effective grants but does not itself confirm canonical current inpatient state. | +| `app/Services/Patient/Messaging/PatientCommunicationEncounterGuard.php` | `8a65438b3779252e88c35ee1dd91cc40e0d0d74e8018f5ac1a1d7a210a617976` | Messaging treats an encounter as current only when linked, active, not discharged, not deleted, and found under a shared lock. | +| `app/Services/Patient/Messaging/PatientCommunicationLifecycleReconciliationService.php` | `2676d63146f7c88ef8a53fc75b4f26abc01a879795edd328e3b3544034787347` | Lifecycle reconciliation distinguishes canonical active, canonical discharged, and unresolved states and uses row locks for mutation safety. | +| `docs/hummingbird/api-contract/hummingbird-patient.v1.yaml` | `fb6220b4ef8eb106223624a9785256fdc1603995f281f430a79897905cb45a1b` | The legacy contract is useful compatibility evidence but carries legacy identifiers, fields, and product decisions. | + +No production database was queried to make this decision. Schema-level and source-level +evidence is sufficient to determine that the present code contains multiple related +definitions but no approved Nightingale source adapter. + +## 4. Current route topology + +| Surface | Current prefix | Owner/audience | Nightingale disposition | +| ---------------------- | ---------------------------------------- | ------------------------------------ | ------------------------------------------------------------------------------------- | +| General and staff APIs | `/api/**`, including `/api/mobile/v1/**` | Zephyrus web/staff products | Prohibited as a Nightingale transport or identity realm | +| Care-pathway API | `/api/care-pathways/v1/**` | Governed pathway service | Potential future internal dependency only; never exposed directly to a patient client | +| Legacy patient API | `/api/patient/v1/**` | Former Hummingbird Patient reference | Preserved as compatibility input; no alias, proxy, redirect, or silent rename | +| Nightingale | `/api/nightingale/v1/**` | Dedicated patient product | Namespace reserved; zero routes and zero OpenAPI paths | + +Laravel currently registers the legacy patient surface in +`app/Providers/RouteServiceProvider.php`. This foundation does not add another route group. +There is no `routes/nightingale.php`, no Nightingale controller, no Nightingale middleware +alias, no Nightingale rate limiter, and no route-response decorator. + +## 5. Route decision + +### 5.1 Reserved namespace + +The canonical reserved prefix is: + +```text +/api/nightingale/v1 +``` + +The version is part of the path because the patient-facing contract needs an explicit, +independently governable compatibility boundary. Product activation, operation activation, +facility/cohort activation, and contract version remain separate decisions. + +Reservation is recorded in three mutually checked artifacts: + +- `config/nightingale.php`; +- `docs/nightingale/api-contract/nightingale-foundation.v0.json`; and +- the held encounter-access candidate. + +The foundation OpenAPI document retains `paths: {}`, a `.invalid` server, no security +schemes, and no client-generation permission. + +### 5.2 First held candidate + +The candidate relative path and operation ID are: + +```text +GET /inpatient-contexts +operationId: listNightingaleInpatientContexts +``` + +The name describes patient-facing purpose rather than exposing a source-system Encounter +resource or a legacy authorization-grant collection. The candidate still returns zero or one +opaque Nightingale handle only, as specified by the held-candidate decision and fixtures. + +The path is not added to `paths`, `RouteServiceProvider`, a route file, or either native app. + +### 5.3 Second held candidate + +After the complete source classification and encounter-access candidate, the next +non-runnable patient-journey candidate is: + +```text +GET /inpatient-contexts/{encounter_handle}/today +operationId: getNightingaleTodayProjection +``` + +It consumes only the Nightingale-owned opaque handle from the separately held +encounter-access candidate. It does not accept or expose a legacy encounter, grant, +principal, patient, projection, or source identifier. Its 68 synthetic outcomes define +field-level release, freshness, uncertainty, language, correction, and offline decisions. + +This extension reserves candidate intent, not runtime behavior. The path remains absent from +the foundation `paths` object, Laravel route registration, native clients, and every +activation mechanism. + +### 5.4 Route-registration preconditions + +Before the route can be registered, evidence must prove at least: + +1. named contract, backend, identity, privacy/security, accessibility, patient, clinical, + legal/HIM, support, operations, and release owners; +2. an approved operation in a non-foundation Nightingale contract; +3. an approved identity/session/recovery design and request authentication adapter; +4. an authoritative current-inpatient source adapter with freshness and outage behavior; +5. complete operation-specific authorization and non-disclosure tests; +6. durable request, denial, safety-failure, and disclosure audit behavior; +7. bounded error, cache, rate, body-size, retry, and correlation contracts; +8. generated-client parity fixtures with native networking still default-off; +9. a reviewed threat model and privacy/security test evidence; +10. patient-language, accessibility, support, downtime, rollback, and controlled integration + approval. + +## 6. Compatibility decision + +### 6.1 Prohibited compatibility mechanisms + +Nightingale will not use: + +- an HTTP redirect from `/api/nightingale/v1` to `/api/patient/v1`; +- a reverse proxy or route alias to the legacy controllers; +- mounting `routes/patient.php` under a second prefix; +- legacy `patient.*` route names under the Nightingale prefix; +- legacy patient feature flags as Nightingale activation controls; +- legacy patient token names, abilities, realm middleware, response decorator, or app + storage as an implicit Nightingale compatibility layer; +- staff `/api/mobile/v1` tokens, routes, scopes, or BFF responses; +- native URL rewriting that turns a Nightingale call into a legacy patient call; or +- fallback to a legacy endpoint when a Nightingale response is unavailable. + +These mechanisms would make product ownership, authorization, audit, rate limits, +deprecation, incident response, and data disclosure depend on two nominally different +products sharing one runtime contract. + +### 6.2 Permitted internal reuse + +Internal reuse is allowed only after classification and may include: + +- product-neutral cryptographic, auditing, rate-limiting, policy-evaluation, and + patient-safe response primitives; +- an internal service that receives a Nightingale-owned input and returns a + Nightingale-owned domain result; +- source adapters that keep raw source identifiers server-side; and +- separately reviewed projections that preserve provenance, release, freshness, + uncertainty, correction, withdrawal, and localization controls. + +Reuse must not expose a legacy model, serializer, controller, middleware stack, route name, +feature flag, token ability, database identifier, or patient-visible label at the Nightingale +boundary. + +### 6.3 Legacy lifecycle + +This ADR does not retire or change `/api/patient/v1`. The reference apps and contract remain +preserved until a separately authorized retirement plan proves: + +- no production consumer depends on the legacy surface; +- necessary records and audit trails meet retention requirements; +- upgrade, support, incident, and rollback paths are approved; and +- removal is independently reviewed and deployed through the protected release workflow. + +## 7. Identity decision + +### 7.1 Independent realm + +Nightingale identity is an independent patient-product realm. A future implementation may +reuse product-neutral infrastructure, but it may not treat any of these as sufficient proof: + +- a Laravel staff `User`; +- a staff mobile token; +- the legacy `PatientPrincipal` model; +- the presence of a legacy Sanctum ability; +- a legacy access grant; +- a patient/source identifier supplied by a client; or +- a matching email, name, date of birth, MRN, encounter identifier, or source record. + +This foundation does not define a credential or identifier class because doing so before the +proofing/session/recovery decision would accidentally canonize an unapproved schema. + +### 7.2 Request-scoped identity port + +`NightingaleIdentityBoundary` returns one of: + +| State | Meaning in this foundation | May continue? | +| --------------- | ------------------------------------------------------------------------------------------------ | --------------------------------- | +| `unavailable` | No approved adapter is configured, or a required identity dependency is unavailable | No | +| `denied` | An approved future adapter completed evaluation but did not establish the allowed identity state | No | +| `verified_self` | A future approved adapter established its defined self relationship | Only to later governed evaluation | + +`verified_self` is not authorization. It must not bypass identity-link ownership, grant, +purpose, source, cardinality, audit, race, or response gates. + +The only implementation supplied now is `UnconfiguredNightingaleIdentityBoundary`; it always +returns `unavailable`. It has no service-container binding and no HTTP caller. + +### 7.3 Identity decisions still required + +An implementation is held until the program approves: + +- identity authority and proofing strength; +- enrollment invitation/challenge lifetime and channel; +- session/access/refresh representation and rotation; +- reauthentication and step-up triggers; +- device binding and compromised-device handling; +- local logout, remote revocation, account removal, and recovery effects; +- merged/corrected identity behavior; +- representative relationship proof, limits, expiration, and revocation; +- minors, sensitive services, confidential encounters, and legal restrictions; +- support-assisted recovery and fraud escalation; +- audit correlation without credential or raw identifier logging; and +- safe, indistinguishable patient-facing errors. + +## 8. Inpatient-source decision + +### 8.1 Why the current model is not adopted directly + +`prod.encounters` is the current operational census spine and is a plausible adapter input, +not a Nightingale authorization source by itself. Reviewed code uses related but not identical +checks: + +- the model `active` scope checks `status = active` and `is_deleted = false`; +- messaging additionally requires `discharged_at IS NULL` and a valid grant source link; +- lifecycle reconciliation treats only `status = discharged` plus a non-null discharge time + as canonical discharge; other combinations are unresolved; and +- the legacy encounter-list service begins from grants rather than a fresh source-state + confirmation. + +The schema comment describes `active | discharged`, but the migration does not add a database +check constraint for encounter status. Unknown values and contradictory status/timestamp +combinations therefore must fail closed. + +### 8.2 Request-scoped source port + +`NightingaleInpatientContextSource` returns only: + +| State | Required interpretation | May continue? | +| ------------------- | --------------------------------------------------------------------------------------------------------- | --------------------------------- | +| `unavailable` | Adapter is unconfigured, source cannot be reached, freshness is unprovable, or required linkage is absent | No | +| `inconsistent` | Source facts conflict, cardinality is unsafe, or an unknown state is encountered | No | +| `confirmed_closed` | The authoritative adapter confirms the context is no longer current | No | +| `confirmed_current` | The approved adapter confirms a current inpatient context at its required freshness | Only to later governed evaluation | + +The port does not return `patient_ref`, `encounter_id`, MRN, source-system identifiers, or a +patient-visible handle. The only implementation supplied now is +`UnconfiguredNightingaleInpatientContextSource`; it performs no database/network query and +always returns `unavailable`. + +### 8.3 Future adapter obligations + +Before an adapter may return `confirmed_current`, it must prove: + +- the authoritative source and source-of-truth precedence; +- identity-to-source linkage and ownership; +- encounter class and admitted/inpatient eligibility; +- facility, unit, cohort, observation, boarding, transfer, and readmission rules; +- admitted, discharged, deleted, cancelled, merged, corrected, and unknown-state handling; +- source-observed time, maximum age, clock skew, and stale behavior; +- duplicate/multiple-current handling; +- transactional or equivalent race protection before serialization; +- source outage, database outage, partial read, timeout, and recovery behavior; +- server-side-only source identifiers and opaque-handle issuance; +- request/evaluation/disclosure audit correlation; and +- no false empty result when the source cannot prove completeness. + +## 9. Fail-closed precondition truth table + +The foundation gate checks only identity and source prerequisites: + +| Identity | Source | Disposition | +| ------------------------ | ---------------------- | --------------------------------- | +| `verified_self` | `confirmed_current` | `continue_to_governed_evaluation` | +| Any other identity state | Any source state | `withhold` | +| Any identity state | Any other source state | `withhold` | + +`continue_to_governed_evaluation` must never be serialized or interpreted as access granted. +At least identity-link, relationship, grant, purpose, cohort, handle, cardinality, race, +audit, and response gates remain. + +## 10. Mechanical enforcement + +The following controls make this ADR falsifiable: + +- `config/nightingale.php` is code-owned, contains no `env()` activation hook, and keeps + route, identity, source, disclosure, mutation, and production states disabled; +- `nightingale-foundation.v0.json` has the reserved namespace but zero paths, no security + scheme, and a non-routable `.invalid` server; +- the held candidate pins its relative path and operation ID while keeping OpenAPI inclusion, + route registration, client generation, networking, and every activation state false; +- default implementations return `unavailable` and execute no database/network code; +- the precondition gate truth table is covered by dependency-free verification and PHPUnit; +- CI negative tests reject route/identity/source/production activation, namespace drift, + operation activation, response widening, legacy fields, and production fixture replay; +- CI rejects a Nightingale route registration in `RouteServiceProvider` and the existence of + `routes/nightingale.php`; and +- the native product boundary continues to reject network clients, staff/legacy identifiers, + and Android internet permission. + +## 11. Consequences + +### Positive + +- Product ownership and compatibility are explicit before runtime code exists. +- The legacy patient and staff realms cannot become Nightingale authentication by accident. +- Source ambiguity and outages withhold rather than become false “no encounter” results. +- A future implementation has narrow ports for independently reviewed adapters. +- Path/operation naming can be reviewed against patient purpose without exposing grant or + source-system resource semantics. + +### Costs + +- Nightingale cannot use the legacy patient controllers as a shortcut. +- Identity and source adapters require explicit designs, tests, and approvals. +- Some reusable legacy behavior will need extraction behind product-neutral services. +- Runtime feature progress remains intentionally slower than a rename until the + authorization and source definitions are complete. + +### Residual risk + +Namespace reservation and default-deny ports do not prove that a future adapter is correct. +The most material open risks are identity/recovery design, representative access, +authoritative inpatient eligibility, source freshness, multi-encounter handling, audit +durability, handle lifecycle, and patient-safe failure behavior. + +## 12. Reversal and supersession + +Changing the prefix, compatibility strategy, identity states, source states, or candidate +path requires: + +1. a superseding ADR; +2. synchronized changes to config, foundation contract, candidate, fixtures/verifiers, plan, + and devlog; +3. renewed collision and compatibility analysis; +4. passing negative tests proving no silent alias or activation; and +5. independent review before route registration. + +Removing the reservation alone does not authorize falling back to the legacy patient or +staff APIs. diff --git a/docs/nightingale/TESTFLIGHT.md b/docs/nightingale/TESTFLIGHT.md new file mode 100644 index 00000000..048ae0c8 --- /dev/null +++ b/docs/nightingale/TESTFLIGHT.md @@ -0,0 +1,140 @@ +# Nightingale TestFlight and iOS distribution + +**Status:** Tooling boundary established; no signing, upload, tester distribution, pilot, +patient access, or release is authorized by this record. + +**Authoritative source root:** `nightingale/iosApp` + +**Scheme:** `Nightingale` + +**Bundle identifier:** `net.acumenus.nightingale` + +## 1. Product boundary + +Nightingale is an independent patient-facing application. The only repository iOS source +permitted to build its bundle identifier is: + +```text +nightingale/iosApp/ +├── Nightingale.xcodeproj +├── Nightingale/ +├── NightingaleTests/ +├── NightingaleUITests/ +├── ExportOptions.plist +└── project.yml +``` + +The former `hummingbird/iosPatientApp` target remains Hummingbird Patient migration +evidence with bundle identifier `net.acumenus.hummingbird.patient`. It must not be renamed, +archived, exported, or uploaded as Nightingale. The non-secret `.appledeploy.example` +registry, native project verifier, and product-boundary CI enforce that separation. + +The repository previously received a mainline change that pointed the legacy target at +the Nightingale bundle identifier. This development stream reverses that collision and +makes the independent source mapping mechanical. Repository comments about an earlier +archive or App Store record are not accepted as signed-artifact, upload, processing, +tester-distribution, or release evidence; this stream has not queried App Store Connect or +performed an upload. + +## 2. Shared helper, independent registry entry + +The repository-level `./testflight.sh` helper supports multiple independently registered +apps. For Nightingale, `.appledeploy.example` requires: + +```sh +NIGHTINGALE_NAME="Zephyrus-Nightingale" +NIGHTINGALE_BUNDLE_ID=net.acumenus.nightingale +NIGHTINGALE_DIR=nightingale/iosApp +NIGHTINGALE_XCODEPROJ=Nightingale.xcodeproj +NIGHTINGALE_SCHEME=Nightingale +``` + +The real `.appledeploy` file is gitignored. App Store Connect API credentials and the +private `.p8` key remain outside the repository. No credential, patient information, or +tester contact belongs in source control. + +The app-local `ExportOptions.plist` configures an App Store Connect export for the declared +Apple team. It deliberately disables Apple-managed build-number mutation so the reviewed +build number remains explicit. An export profile is configuration, not release approval. + +## 3. Safe command progression + +The helper exposes: + +```bash +./testflight.sh help +./testflight.sh doctor +./testflight.sh apps +./testflight.sh builds nightingale +./testflight.sh status nightingale +./testflight.sh groups nightingale +``` + +The following command creates a signed local archive and IPA but does not upload: + +```bash +./testflight.sh ship nightingale --build --no-upload +``` + +Upload and tester distribution are state-changing external release actions: + +```bash +./testflight.sh ship nightingale --build --wait +./testflight.sh distribute nightingale +``` + +They require a separately authorized release decision after every gate below is closed. +Neither this document nor green CI grants that authorization. + +## 4. Required pre-upload evidence + +- [ ] Protected `main` contains the reviewed Nightingale change and is clean and + synchronized. +- [ ] CI is green for the exact source SHA with no rerun masking an unexplained failure. +- [ ] The generated Xcode project matches `nightingale/iosApp/project.yml`. +- [ ] Debug unit and UI suites pass on the supported iOS Simulator matrix. +- [ ] A device Release archive builds from `nightingale/iosApp`, scheme `Nightingale`. +- [ ] The archive and exported IPA identify only `net.acumenus.nightingale`. +- [ ] The archive contains no legacy `hummingbird.patient` identity, Debug test hook, + pseudolocale, synthetic patient, credential, or unapproved endpoint. +- [ ] The exact archive and IPA SHA-256 values are recorded. +- [ ] Marketing version and build number are greater than every accepted prior build for + that marketing version. +- [ ] Code-signing certificate, provisioning profile, entitlements, team, and expiration + are inspected from the exact archive. +- [ ] Privacy manifest, App Store privacy answers, export-compliance answer, support URL, + privacy URL, screenshots, subtitle, description, age rating, and accessibility + claims match the exact binary and approved product state. +- [ ] The seven-background catalog has durable rights/attribution evidence and named + patient/advisor accessibility approval for external distribution. +- [ ] Identity, source, content, communication, accessibility, privacy/security, clinical + safety, support, and release approvals are recorded by named owners. +- [ ] Pilot cohort, facility/unit scope, language, exclusions, support hours, expiry, + rollback, kill switch, incident routing, and go/no-go authority are signed. + +## 5. Required post-upload evidence + +- [ ] App Store Connect reports the exact bundle identifier, marketing version, and build + number. +- [ ] The processed build maps to the recorded source, archive, and IPA hashes. +- [ ] Processing completes without unexpected entitlement, privacy, symbol, or compliance + changes. +- [ ] Only the approved internal or external tester group receives the build. +- [ ] External Beta App Review status is recorded when applicable. +- [ ] Installation and upgrade are tested on supported physical devices without erasing + protected patient state or leaking the privacy cover. +- [ ] Launch, background/foreground, orientation, maximum text, VoiceOver, reduced motion, + increased contrast, imagery-hidden behavior, offline behavior, and rollback are + verified on the distributed build. +- [ ] Any connected capability is reauthenticated, reauthorized, source-freshness checked, + correction/retraction aware, and generic on withheld or revoked access. +- [ ] Distribution, support, security, privacy, and clinical owners sign the final + go/no-go record before a pilot is activated. + +## 6. Current hold + +Nightingale is currently an offline, default-deny foundation. It has no approved identity +provider, current-inpatient source adapter, patient API operation, clinical projection, +message capability, notification provider, or pilot cohort. A signed archive can be useful +engineering evidence, but it must not be distributed to patients or described as a +patient-ready product until the plan’s independent approvals and pilot gates are complete. diff --git a/docs/nightingale/TODAY-PROJECTION-CANDIDATE-DECISION-2026-07-26.md b/docs/nightingale/TODAY-PROJECTION-CANDIDATE-DECISION-2026-07-26.md new file mode 100644 index 00000000..382ed05b --- /dev/null +++ b/docs/nightingale/TODAY-PROJECTION-CANDIDATE-DECISION-2026-07-26.md @@ -0,0 +1,668 @@ +# Nightingale Today projection candidate decision + +**Status:** Held, non-runnable contract and synthetic fixture specification only + +**Decision date:** 2026-07-26 + +**Candidate operation:** `GET /inpatient-contexts/{encounter_handle}/today` + +**Operation ID:** `getNightingaleTodayProjection` + +**Runtime status:** No OpenAPI path, Laravel route, controller, provider, source adapter, +database query, generated client, native network call, patient record, non-production +integration, production integration, feature activation, or deployment is authorized or +present. + +## 1. Artifacts + +- [Candidate decision record](./api-contract/candidates/today/v0/candidate.json) +- [Synthetic response and failure fixtures](./api-contract/candidates/today/v0/fixtures.json) +- [Empty/default-off contract foundation](./api-contract/nightingale-foundation.v0.json) +- [Encounter-access held candidate](./ENCOUNTER-ACCESS-CANDIDATE-DECISION-2026-07-26.md) +- [Route, compatibility, identity, and source ADR](./ROUTE-COMPATIBILITY-IDENTITY-SOURCE-ADR-2026-07-26.md) +- [Contract ownership and authorization matrix](./CONTRACT-OWNERSHIP-AND-AUTHORIZATION-MATRIX-2026-07-26.md) +- [Complete source-classification index](./MIGRATION-CLASSIFICATION-2026-07-26.md) + +The candidate and fixture artifacts are generated by +`scripts/ci/build-nightingale-today-candidate.mjs` and independently checked by +`scripts/ci/verify-nightingale-today-candidate.mjs`. CI runs the verifier with 24 adversarial +self-tests. + +## 2. Decision outcome + +The first patient-journey contract candidate after encounter discovery is a Nightingale-owned +Today projection. It answers only: + +> What released information is available to help this authenticated inpatient understand +> today? + +It does not answer what clinicians privately believe, what the EHR contains, what the final +care plan will be, whether a test or discharge will happen at a particular time, or whether +the absence of a released item means that no care is planned. + +The candidate establishes six core rules: + +1. **Every patient-visible value carries its own governance context.** Release, freshness, + uncertainty, language, correction, and offline behavior cannot be represented only once + at document level. +2. **Empty and unavailable are different states.** A released-empty list means only that no + items were released for that section after a complete governed evaluation. It never means + that no care is planned. A not-available section contains no content and directs the + patient to the care team. +3. **Only Nightingale-owned opaque handles cross the boundary.** The request uses the held + encounter handle, and schedule items use new opaque item handles. No encounter, grant, + principal, patient, staff, FHIR, EHR, or source-system identifier is exposed. +4. **Corrections replace; retractions withhold.** An effective released replacement may show + a source-free correction notice. The response never exposes the withdrawn value, actor, + reason, target identifier, or internal correction record. +5. **The server remains authoritative at disclosure time.** Identity, context, operation, + release, field governance, race recheck, and durable audit gates are evaluated before one + atomic no-store response. +6. **This is a design candidate, not implementation approval.** The executable Nightingale + foundation still contains zero paths and every activation field remains false. + +## 3. Why a new contract is required + +The legacy Hummingbird Patient Today surface demonstrates useful patient-experience ideas, +but its contract cannot be renamed or copied into Nightingale. + +### 3.1 Aggregate projection context can mislabel individual fields + +The legacy response places one `uncertainty`, one `provenance`, and one aggregate freshness +record around a composite document. Today may combine: + +- a schedule item from one source observation; +- a location label from another; +- discharge outlook produced and reviewed at a different time; +- translated prose released under a separate workflow; and +- a corrected field whose predecessor was withdrawn. + +One document-level freshness or provenance statement can therefore make a field appear newer, +more certain, more directly sourced, or more recently reviewed than it is. Nightingale +requires a complete context beside every displayed value. + +### 3.2 Legacy identifiers are product and authorization internals + +The legacy projection returns `projection_uuid` and `encounter_uuid`; its broader patient +surface also uses grant UUIDs and raw authorization scopes. Those values are unnecessary for +rendering Today and create correlation, caching, compatibility, and inference obligations. + +The candidate accepts only a Nightingale encounter handle already issued by the separately +held encounter-access operation and returns only a Nightingale content revision plus opaque +schedule-item handles. + +### 3.3 Native clients currently represent different subsets + +The legacy iOS model decodes care location and discharge outlook. The Android model decodes +headline, summary, schedule, next steps, and notices but omits those two Today structures. +Both clients turn a composite response into presentation models with different loss and +default behavior. + +The candidate does not claim native parity. It instead creates a canonical fixture boundary +that future generated models must decode identically before networking can be enabled. + +### 3.4 First-record selection is not an encounter rule + +Both legacy native clients select the first encounter returned by the legacy list endpoint. +Nightingale has already rejected that behavior. Today is addressed through the one opaque +context handle produced by the separately held zero-or-one self-inpatient candidate. A +client-supplied source encounter identifier and a server-selected “first record” are both +prohibited. + +### 3.5 A general content allowlist is not field approval + +The legacy content guard has valuable defensive patterns: + +- explicit top-level keys; +- recursive forbidden identifiers; +- bounded patient-language value shapes; +- release, uncertainty, provenance, and relationship checks; and +- patient-state vocabulary validation. + +Those patterns are evidence, not clinical approval. A field still needs an authoritative +source, release authority, freshness policy, uncertainty wording, correction/retraction +behavior, language approval, and offline rule. Nightingale makes those decisions explicit at +the field boundary. + +### 3.6 “No released items” must not be mistaken for “nothing planned” + +The iOS and Android reference screens both use safe prose when Today is absent, but the +underlying list structure does not itself distinguish: + +- no planned care; +- no released care; +- incomplete source evaluation; +- unavailable source; +- a withheld sensitive field; +- a retracted release; or +- a client decode failure. + +The candidate uses section states and response errors to prevent those conditions from +collapsing into a misleading empty list. + +## 4. Evidence baseline + +The complete Hummingbird Patient product universe contains 256 classified source files with +inventory digest +`a307e1957df7ef78eb61a9a9123f3902fd8929ebb3aaeb4dce48f2c88fb4a881`. +This candidate additionally pins the exact bytes of 14 direct Today inputs. + +| Direct source | SHA-256 | Candidate use | +| --------------------------------------------------------------------------------------- | ------------------------------------------------------------------ | ----------------------------------------------------------------------------------- | +| `docs/hummingbird/api-contract/hummingbird-patient.v1.yaml` | `fb6220b4ef8eb106223624a9785256fdc1603995f281f430a79897905cb45a1b` | Legacy path, schemas, envelope, authorization, release, and error evidence | +| `app/Http/Controllers/Api/Patient/EncounterProjectionController.php` | `8d55765655a35ea87d809c5bf964a4e8b25ecb6a25081788d05319bbbfbacaf0` | Generic non-disclosure response pattern | +| `app/Services/Patient/Projection/PatientProjectionDisclosureService.php` | `9d5b5dfea90985f022b190baee18eb2826cf395b67c936576e50ef25fa418049` | Release selection, correction replacement, audit, and aggregate-context limitations | +| `app/Services/Patient/Projection/PatientProjectionContentGuard.php` | `55c1ef8f34750f4831eb98dd519824ba61344e2fe56a94c6c855c2db90772d9a` | Content allowlist, forbidden source values, and legacy vocabulary | +| `app/Policies/Patient/PatientEncounterProjectionPolicy.php` | `582267c8e7aff6d8e68574319907654fcf3111fb93834221cfffc48943362209` | Defense-in-depth relationship, release, policy, and content-action checks | +| `app/Models/Patient/PatientEncounterProjection.php` | `b2675fea1fa6d2a3362ce54bb732858da221604f1f388b766d8fea1f6f449eb0` | Legacy projection persistence semantics | +| `database/migrations/2026_07_19_000200_create_patient_experience_projection_kernel.php` | `20c55f5d563bfd3342ec4e833d71dd32b4f04c49fe1c455a96b05a03aae257ef` | Schema-level release, version, relationship, and content evidence | +| `tests/Feature/Patient/PatientProjectionApiTest.php` | `3775071196794e2856f05e35eeac86f7dd6ea1b074dd08549318416d171a9e2e` | Non-disclosure, release, correction, no-store, and audit test patterns | +| iOS `PatientAPIModels.swift` | `93c3250b7292c9abe17e3bb932e39c489332c8becc321663b4b8365a1e468759` | Legacy decoding and field coverage | +| iOS `PatientTodayView.swift` | `63f2003a80cc316f4f47d76cc0a1e56b2d3c77875004be78c27d252d0fcae663` | Patient-language, released-empty, provenance, and safety-notice behavior | +| iOS `PatientAppViewModel.swift` | `74918da1c9af1a263b4535dec933c4c6acf4384bb7afa195997a26e5e1c37694` | First-record selection and aggregate presentation mapping | +| Android `PatientApiModels.kt` | `a1932c3dc2f037f4006c72e083d57bc6219f63de42f348c7909737707d25e115` | Legacy Android decoding and Today field omissions | +| Android `PatientSessionCoordinator.kt` | `10bfbe8782746390ea71242d1c95ff5f09809b61be40533f91cba1e9acfaada2` | First-record selection and composite-context mapping | +| Android `PatientExperienceScreen.kt` | `7b2345a9735451514aec54fe52bf04d28964935c902bb1b13676e76e8065911d` | Patient-facing empty-state and explanatory behavior | + +The verifier recomputes every direct checksum. Any source-byte drift fails the candidate +gate and requires explicit re-analysis. + +No production database, deployed configuration, live response, patient, encounter, grant, +principal, session, projection, content release, audit event, or mobile distribution record +was read or changed for this decision. + +## 5. Operation boundary + +The held relative path is: + +```text +GET /inpatient-contexts/{encounter_handle}/today +operationId: getNightingaleTodayProjection +``` + +It would sit under the already reserved `/api/nightingale/v1` namespace only after approval. +The path parameter must match `^ntg_enc_[a-z2-7]{50}$`. + +The request deliberately has: + +- no query parameters; +- no body; +- no client-supplied locale override in this initial candidate; +- no source identifier; +- no legacy identifier; +- no patient, principal, grant, or projection identifier; +- no authorization scope supplied by the client; and +- no permission for durable handle storage on the client. + +The response locale comes from a future approved account/session preference decision. That +decision remains held; the fixtures use synthetic `en-US` only. + +## 6. Authorization and disclosure sequence + +An eventual implementation must evaluate all gates in this order or a demonstrably +equivalent fail-closed order: + +```text +active contract operation + -> product / operation / facility / cohort gates + -> Nightingale realm, principal, session, and assurance + -> verified, unmerged, self identity link + -> opaque context handle owned by that principal + -> authoritative current inpatient confirmation + -> server-side Today capability + -> effective release policy + -> effective patient-language release + -> every displayed field's governance context + -> identity, context, capability, and release race recheck + -> durable request evaluation audit + -> durable field-disclosure audit + -> one atomic no-store response +``` + +No client-side cached capability, section presence, hidden control, legacy scope, encounter +ordering, or response from another product is authority. + +The candidate is self-only. Representative access and sensitive-encounter behavior remain +held because their proof, consent, expiration, restriction, and disclosure rules are not +approved. + +## 7. Response envelope + +A successful response contains: + +- `data.kind = "today"`; +- one opaque `content_revision` matching + `^ntg_tdyrev_[a-z2-7]{50}$`; +- exactly eight named section objects; +- an optional source-free `revision_notice`; +- request, generation, policy, authorization-evaluation, inpatient-context-evaluation, + locale, and completeness metadata; and +- an empty `links` object in this candidate. + +It does not contain document-level provenance, freshness, or uncertainty. It does not expose +an encounter handle again, because the request already supplies the context. It does not +return an internal projection identifier. + +Every template requires: + +```text +Cache-Control: private, no-store, max-age=0 +Pragma: no-cache +X-Robots-Tag: noindex, nofollow, noarchive +Vary: Authorization, Accept-Language +Content-Type: application/json +``` + +The candidate permits no durable client cache and declares every governed field online-only. +Offline retention, screenshots, operating-system backups, notification payloads, and +background refresh remain separately governed work. + +## 8. Field-level governance + +Every displayed value has exactly two members: + +```json +{ + "value": "patient-visible value", + "context": { + "release": {}, + "freshness": {}, + "uncertainty": {}, + "language": {}, + "correction": {}, + "offline": {} + } +} +``` + +### 8.1 Release + +Each field records: + +- `state = released`; +- an ISO-8601 `released_at`; +- the Nightingale patient-language content-policy version. + +A document cannot make an unreleased field displayable by being released as a whole. +Clinical approval and patient-language approval are separate prerequisites even when one +workflow eventually captures both. + +### 8.2 Freshness + +The candidate permits: + +- `current`; or +- `approved-stale`. + +Approved-stale display requires a patient notice at that field. `unknown` is an uncertainty +level, not a permitted freshness shortcut. Mandatory content with unknown freshness fails +closed. A future freshness threshold must be approved per source/field class; this candidate +does not invent one. + +### 8.3 Uncertainty + +Each field has: + +- `low`, `medium`, `high`, or `unknown`; +- a patient-language explanation; and +- a boolean stating whether it can change. + +This context belongs to the field. A confirmed room and an estimated time window must not +inherit the same certainty. + +### 8.4 Language + +Each field has: + +- a BCP 47-style locale matching the response locale; +- `approved-source-language` or `approved-translation`; and +- `plain_language_review = approved`. + +Machine translation, silent locale fallback, untranslated staff prose, and a language label +without content approval are not accepted. + +### 8.5 Correction + +Each field is `original` or `corrected`. A corrected field requires a patient notice. The +optional root revision notice can tell the patient that part of Today changed, but it cannot +expose: + +- the prior value; +- the correction actor; +- an internal reason code; +- the target/replacement record; +- an internal timestamp beyond the released field context; or +- staff review metadata. + +### 8.6 Offline + +Every field is `online-only`, with `durable_storage_permitted = false`. This is intentionally +stricter than a future offline design. A later proposal must separately address: + +- session memory; +- protected local storage; +- expiration and revocation; +- device transfer and backup; +- screenshots and screen recording; +- push payloads; +- background refresh; +- correction/retraction propagation; and +- safe behavior after discharge or remote logout. + +## 9. Section semantics + +The candidate names eight sections: + +1. `headline`; +2. `summary`; +3. `schedule`; +4. `next_steps`; +5. `care_location`; +6. `discharge_outlook`; +7. `questions`; and +8. `notices`. + +Headline and summary must be released and governed for every 200 response. The six other +sections support explicit availability. + +### 9.1 Released + +The section contains governed values. This does not imply that it contains all operational +or clinical information—only the complete set authorized for that released patient section. + +### 9.2 Released-empty + +List sections may be released-empty only when the governed evaluation completed and produced +zero released items. The response must include a notice that this does not mean no care is +planned. + +Released-empty with any item is a contract violation. + +### 9.3 Not-available + +The section contains no content and provides a patient-language care-team fallback. It does +not identify whether the reason is source, release, policy, sensitivity, freshness, or +translation. + +Not-available with hidden or partial content is a contract violation. + +## 10. Schedule items + +A schedule item uses an opaque handle matching +`^ntg_tdyitem_[a-z2-7]{50}$`. Handles must be unique within the response and carry no source +meaning. + +Every item field is separately governed: + +- label; +- detail; +- status; +- time window; +- timing confidence; +- preparation; +- whether it can change. + +The held status candidates are: + +- requested; +- planned; +- confirmed; +- in progress; +- completed; +- delayed; and +- canceled. + +The candidate artifact uses machine values with hyphenated `in-progress`. This is not a +clinically approved vocabulary release. Labels and cross-platform unknown-value behavior +remain subject to the patient-state vocabulary approval gate. + +Timing confidence is: + +- confirmed; +- estimated; or +- unknown. + +An exact time or ETA claim is prohibited in this candidate. Patient-facing time windows must +be reviewed language and carry their own freshness and uncertainty context. + +## 11. Care location + +Care location can contain governed facility, unit, and room display names plus a governed +status: + +- current; +- updating; or +- unknown. + +It cannot contain a source location identifier, bed-management priority, capacity state, +staffing state, worker location, isolation detail, or a future destination that is not +released for the patient. + +`updating` and `unknown` are first-class patient states. The client must not keep a prior +room label visible under a newer aggregate “current” badge when the field itself is stale or +withdrawn. + +## 12. Discharge outlook + +Discharge outlook can contain: + +- a governed estimated range; +- governed timing confidence; +- governed readiness-topic values; +- governed remaining-step values; and +- governed `can_change`. + +It is never: + +- a discharge order; +- a promised date; +- a prognosis; +- an automatic inference from length of stay; +- a staff capacity or placement priority; +- a claim that every unresolved need is shown; or +- a substitute for speaking with the care team. + +Unknown timing is a valid released patient state when approved wording explains it. + +## 13. Release, correction, and retraction + +The candidate requires one effective policy and one coherent effective release. + +| Source state | Patient result | +| ------------------------------------------------- | ----------------------------------------------------------- | +| No released projection | Generic `404 not_found` | +| Draft only | Generic `404 not_found` | +| Future release | Generic `404 not_found` | +| Effective retraction | Generic `404 not_found` | +| Correction without effective released replacement | Generic `404 not_found` | +| Effective corrected replacement | `200` with only replacement values and a source-free notice | +| Conflicting effective releases | Generic `503 temporarily_unavailable` | +| Policy-version mismatch | Generic `503 temporarily_unavailable` | + +A correction never causes the withdrawn value to reappear from cache. A retraction never +falls back to an older release without a new independent release decision. + +## 14. Error and non-disclosure behavior + +The candidate defines: + +| Code | Status | Intended use | +| ------------------------------- | -----: | ------------------------------------------------------------------------------------------------------------------------------------ | +| `authentication_required` | 401 | Approved Nightingale authentication is missing, expired, or revoked | +| `access_unavailable` | 403 | The account, relationship, cohort, identity link, or sensitive-service state cannot use this experience | +| `not_found` | 404 | Product/operation/facility disabled; unknown or wrong-principal handle; closed context; capability denied; unavailable release state | +| `account_state_requires_review` | 409 | Opaque-handle mapping is internally inconsistent | +| `rate_limited` | 429 | Approved principal-specific request limit exceeded | +| `temporarily_unavailable` | 503 | Source, database, content-integrity, field-governance, language, freshness, audit, or serialization safety failure | + +Unknown, wrong-principal, malformed, closed, and authorization-race handles share the same +generic no-content surface. Responses never echo a handle or disclose whether an encounter, +release, correction, field, or source record exists. + +The candidate intentionally avoids `500` as a patient contract. Internal failures map to a +bounded, patient-safe unavailable response after best-effort safety audit. + +## 15. Audit requirements + +Every 200 response requires: + +1. one durable request evaluation event; and +2. one durable field-disclosure event that can prove which governed release set was + disclosed without storing patient-visible values. + +The audit contract prohibits: + +- raw patient-visible values; +- raw context or item handles; +- source identifiers; +- staff actor identifiers; +- free text; and +- credentials. + +Success fails closed if either durable audit cannot be recorded. Denial and safety-failure +audits are best effort so the audit sink does not become a new existence or availability +oracle. + +The exact correlation, retention, access, legal-hold, incident, and patient-request semantics +remain approval work. + +## 16. Synthetic fixture coverage + +The fixture set contains 68 cases and 18 response templates. + +| Group | Cases | What is proven | +| ---------------------------------------- | -----: | ------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| Governed success variants | 12 | Full, minimal, explicit empty, explicit unavailable, approved stale, correction, language, uncertainty, location, and field-context differences | +| Product/identity/authorization/lifecycle | 20 | Default-off behavior, Nightingale realm, session and link failures, self-only boundary, handle non-disclosure, context closure/race, and mapping integrity | +| Source/release/content governance | 24 | Source and database outage, capability, release states, policy conflicts, clinical/language/freshness gates, mandatory fields, content safety, and vocabulary | +| Structure/audit/serialization/rate | 12 | Item/section contradictions, timestamp coherence, correction privacy, durable audit failure, atomic serialization, and throttling | +| **Total** | **68** | Synthetic candidate coverage only | + +Representative examples include: + +- a synthetic request template containing only the held method, namespace, path, opaque + encounter handle, empty query, null body, and an authentication-context requirement; +- full current response; +- minimal response with every optional section not available; +- released-empty schedule with a non-absence notice; +- approved stale time window with a field notice; +- effective correction replacement; +- wrong-principal and unknown handles with indistinguishable 404s; +- source outage versus no released projection; +- draft, future, retracted, and correction-without-replacement states; +- missing clinical, language, translation, and freshness approval; +- aggregate-only context rejection; +- internal identifier and staff-only content rejection; +- invalid vocabulary and duplicate opaque handle rejection; +- released-empty-with-items and not-available-with-content contradictions; +- audit failure; and +- response serialization failure. + +Fixtures are synthetic-only, contain no real patient data, and cannot be replayed against +production. + +## 17. Verifier behavior + +The verifier checks: + +- the executable foundation remains version `0.0.0-governance` with zero paths; +- routes, route registration, clients, and disclosure remain disabled; +- every candidate activation and permission remains false; +- candidate, evidence, fixture, request, response, and field object keys remain exact; +- the path, operation ID, request shape, opaque-handle patterns, section names, field + contexts, vocabularies, failure codes, audit rules, and approval gates remain exact; +- the classified product-universe count and digest remain pinned; +- all 14 direct source bytes match their recorded SHA-256; +- all 68 case IDs, expected templates, and audit modes remain exact; +- all 18 response templates have exact no-store headers and bounded metadata; +- response links remain empty, failure codes match their templates, and fixture timestamps + remain deterministic and coherently ordered; +- every success field has complete release, freshness, uncertainty, language, correction, + and offline context; +- approved stale and corrected values have patient notices; +- response and field locales match; +- schedule handles are valid and unique; +- section state/content combinations are coherent; +- error bodies contain no patient data; +- candidate fixtures contain no legacy path, credential, or prohibited identifier fields; + and +- no production host or credential literal appears. + +The 24 negative self-tests mutate: + +1. the held path; +2. OpenAPI inclusion; +3. production activation; +4. root aggregate freshness; +5. field context removal; +6. stale notice removal; +7. field locale; +8. released-empty content; +9. production replay permission; +10. foundation paths; +11. source checksum; +12. fixture coverage; and +13. durable client caching; +14. full-current field freshness; +15. minimal-response optional content; +16. optional-unavailable location content; +17. the approved-stale semantic state; +18. the corrected-replacement semantic state; +19. all-fields translation release; +20. all-fields source-language release; +21. delayed status and uncertainty; +22. unknown, changeable discharge timing; +23. updating care-location state; and +24. independently varied field contexts. + +Each mutation must be rejected for the expected reason. + +## 18. Approval gates before implementation + +No route, adapter, client, or source query may be implemented until named independent owners +approve: + +- product and contract ownership; +- backend boundary; +- Nightingale identity, proofing, enrollment, session, assurance, recovery, and self link; +- authoritative current-inpatient source, facility, cohort, lifecycle, freshness, linkage, + and outage behavior; +- Today operation authorization and non-disclosure; +- every field's source, release, freshness, uncertainty, correction, translation, and + offline rule; +- patient-state and timing vocabulary; +- clinical content generation, review, release, correction, and retraction; +- privacy, security, threat model, and audit; +- accessibility and human factors; +- patient-advisor and language review; +- legal/HIM; +- support and downtime; +- operations, incident response, and rollback; and +- default-off non-production integration. + +The approved evidence must identify people or accountable roles, decision dates, scope, +conditions, residual risks, expiry/re-review triggers, and rollback relevance. A checked +box without evidence is not approval. + +## 19. Work deliberately not completed + +This slice does not: + +- add Today to the executable OpenAPI document; +- create a Laravel route, controller, middleware, policy, provider, adapter, query, model, or + migration; +- choose a production identity or inpatient source; +- approve a clinical field or patient-language string; +- finalize Nightingale patient-state vocabulary; +- generate Swift or Kotlin models; +- enable networking, background refresh, push, offline storage, or notifications; +- create a patient or projection fixture in any database; +- access production; +- authorize a pilot; +- approve app-store distribution; +- merge to protected `main`; or +- deploy. + +The next implementation transition remains blocked on named identity/source approvals and +the field-level clinical/content matrix. This candidate reduces ambiguity; it does not +convert missing governance into authorization. diff --git a/docs/nightingale/api-contract/candidates/encounter-access/v0/candidate.json b/docs/nightingale/api-contract/candidates/encounter-access/v0/candidate.json new file mode 100644 index 00000000..9c63c930 --- /dev/null +++ b/docs/nightingale/api-contract/candidates/encounter-access/v0/candidate.json @@ -0,0 +1,146 @@ +{ + "artifact_kind": "nightingale-nonrunnable-api-candidate", + "candidate_id": "nightingale.encounter-access.v0-candidate", + "status": "held-no-operation", + "purpose": "Discover zero or one currently eligible self inpatient context without exposing authorization or source-system internals.", + "operation": { + "method": "GET", + "path": "/inpatient-contexts", + "operation_id": "listNightingaleInpatientContexts", + "openapi_inclusion": false, + "route_namespace_reserved": true, + "route_registration_permitted": false, + "client_generation_permitted": false, + "network_client_permitted": false + }, + "activation": { + "product": false, + "operation": false, + "identity": false, + "disclosure": false, + "nonproduction_integration": false, + "production": false + }, + "audience": { + "permitted_relationships": ["self"], + "representative_access": "held", + "maximum_eligible_inpatient_contexts": 1 + }, + "response": { + "success_status": 200, + "maximum_encounters": 1, + "entry_fields": ["encounter_handle"], + "encounter_handle_pattern": "^ntg_enc_[a-z2-7]{50}$", + "metadata_fields": [ + "request_id", + "generated_at", + "policy_version", + "authorization_evaluated_at", + "completeness" + ], + "excluded_legacy_fields": [ + "encounter_uuid", + "grant_uuid", + "relationship", + "scopes", + "valid_from", + "expires_at", + "version", + "principal_uuid", + "identity_link_uuid", + "source_encounter_id", + "source_encounter_ref", + "source_system_key" + ], + "cache_control": "private, no-store, max-age=0", + "state_vocabulary_applies": false, + "durable_client_storage_permitted": false + }, + "eligibility_gates": [ + "approved-candidate-operation-in-active-contract", + "product-operation-facility-cohort-default-off-gates", + "patient-realm-principal-session-and-assurance", + "verified-unmerged-identity-link-owned-by-principal", + "self-relationship-only", + "approved-inpatient-purpose-of-use", + "active-nonrevoked-effective-grant", + "current-source-encounter-linkage-and-status", + "nightingale-owned-random-handle-mapping", + "single-eligible-context-in-initial-release", + "authorization-recheck-before-serialization", + "durable-request-and-disclosure-audit", + "patient-safe-no-store-response" + ], + "failure_codes": { + "not_found": 404, + "authentication_required": 401, + "access_unavailable": 403, + "account_state_requires_review": 409, + "rate_limited": 429, + "temporarily_unavailable": 503 + }, + "audit": { + "request_event": "nightingale.encounter_access.evaluated", + "disclosure_event": "nightingale.encounter_handle.disclosed", + "success_and_empty_result_require_durable_audit": true, + "raw_handle_recording_permitted": false, + "source_identifier_recording_permitted": false, + "free_text_recording_permitted": false + }, + "required_fixture_case_ids": [ + "eligible_self_single", + "no_candidate_grants", + "revoked_grant_omitted", + "suspended_grant_omitted", + "pending_grant_omitted", + "closed_grant_omitted", + "expired_status_grant_omitted", + "revoked_timestamp_omitted", + "future_validity_omitted", + "expired_window_omitted", + "wrong_principal_omitted", + "purpose_mismatch_omitted", + "representative_relationship_held", + "source_encounter_closed_omitted", + "product_disabled", + "operation_disabled", + "authentication_missing", + "wrong_identity_realm", + "principal_inactive", + "principal_locked", + "session_missing", + "session_expired", + "session_revoked", + "identity_link_missing", + "identity_link_revoked", + "identity_link_merged", + "identity_principal_mismatch", + "unknown_relationship", + "unknown_grant_status", + "unknown_purpose_of_use", + "malformed_nightingale_handle", + "nightingale_handle_collision", + "source_link_missing", + "source_system_unavailable", + "database_unavailable", + "request_audit_unavailable", + "disclosure_audit_unavailable", + "multiple_eligible_contexts", + "grant_changed_before_serialization", + "policy_version_mismatch", + "malformed_scope_registry", + "rate_limited" + ], + "approval_gates": [ + "named-product-contract-backend-identity-privacy-security-accessibility-support-and-release-owners", + "route-and-compatibility-adr", + "identity-proofing-session-recovery-and-representative-decision", + "facility-unit-cohort-and-inpatient-source-definition", + "opaque-handle-generation-storage-rotation-and-collision-design", + "authorization-and-nondisclosure-implementation-tests", + "canonical-backend-ios-android-fixture-parity", + "threat-model-and-privacy-security-review", + "patient-advisor-accessibility-language-and-support-review", + "default-off-nonproduction-integration-and-rollback" + ] +} diff --git a/docs/nightingale/api-contract/candidates/encounter-access/v0/fixtures.json b/docs/nightingale/api-contract/candidates/encounter-access/v0/fixtures.json new file mode 100644 index 00000000..93b84232 --- /dev/null +++ b/docs/nightingale/api-contract/candidates/encounter-access/v0/fixtures.json @@ -0,0 +1,551 @@ +{ + "fixture_schema": "nightingale.encounter-access.candidate-fixtures.v0", + "candidate_id": "nightingale.encounter-access.v0-candidate", + "synthetic_only": true, + "production_replay_permitted": false, + "response_templates": { + "success_one": { + "status": 200, + "headers": { + "Cache-Control": "private, no-store, max-age=0", + "Pragma": "no-cache", + "X-Robots-Tag": "noindex, nofollow, noarchive", + "Vary": "Authorization", + "Content-Type": "application/json" + }, + "body": { + "data": { + "encounters": [ + { + "encounter_handle": "ntg_enc_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + } + ] + }, + "meta": { + "request_id": "ntg_req_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "generated_at": "2026-07-26T15:00:00Z", + "policy_version": "nightingale-encounter-access-policy.v0-candidate", + "authorization_evaluated_at": "2026-07-26T15:00:00Z", + "completeness": "complete" + }, + "links": {} + } + }, + "success_empty": { + "status": 200, + "headers": { + "Cache-Control": "private, no-store, max-age=0", + "Pragma": "no-cache", + "X-Robots-Tag": "noindex, nofollow, noarchive", + "Vary": "Authorization", + "Content-Type": "application/json" + }, + "body": { + "data": { + "encounters": [] + }, + "meta": { + "request_id": "ntg_req_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "generated_at": "2026-07-26T15:00:00Z", + "policy_version": "nightingale-encounter-access-policy.v0-candidate", + "authorization_evaluated_at": "2026-07-26T15:00:00Z", + "completeness": "complete" + }, + "links": {} + } + }, + "not_found": { + "status": 404, + "headers": { + "Cache-Control": "private, no-store, max-age=0", + "Pragma": "no-cache", + "X-Robots-Tag": "noindex, nofollow, noarchive", + "Vary": "Authorization", + "Content-Type": "application/json" + }, + "body": { + "data": null, + "error": { + "code": "not_found", + "message": "This experience is not available." + }, + "meta": { + "request_id": "ntg_req_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "generated_at": "2026-07-26T15:00:00Z", + "policy_version": "nightingale-encounter-access-policy.v0-candidate", + "authorization_evaluated_at": "2026-07-26T15:00:00Z", + "completeness": "withheld" + }, + "links": {} + } + }, + "authentication_required": { + "status": 401, + "headers": { + "Cache-Control": "private, no-store, max-age=0", + "Pragma": "no-cache", + "X-Robots-Tag": "noindex, nofollow, noarchive", + "Vary": "Authorization", + "Content-Type": "application/json" + }, + "body": { + "data": null, + "error": { + "code": "authentication_required", + "message": "Sign in is required." + }, + "meta": { + "request_id": "ntg_req_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "generated_at": "2026-07-26T15:00:00Z", + "policy_version": "nightingale-encounter-access-policy.v0-candidate", + "authorization_evaluated_at": "2026-07-26T15:00:00Z", + "completeness": "withheld" + }, + "links": {} + } + }, + "access_unavailable": { + "status": 403, + "headers": { + "Cache-Control": "private, no-store, max-age=0", + "Pragma": "no-cache", + "X-Robots-Tag": "noindex, nofollow, noarchive", + "Vary": "Authorization", + "Content-Type": "application/json" + }, + "body": { + "data": null, + "error": { + "code": "access_unavailable", + "message": "This experience is not available for this account." + }, + "meta": { + "request_id": "ntg_req_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "generated_at": "2026-07-26T15:00:00Z", + "policy_version": "nightingale-encounter-access-policy.v0-candidate", + "authorization_evaluated_at": "2026-07-26T15:00:00Z", + "completeness": "withheld" + }, + "links": {} + } + }, + "account_state_requires_review": { + "status": 409, + "headers": { + "Cache-Control": "private, no-store, max-age=0", + "Pragma": "no-cache", + "X-Robots-Tag": "noindex, nofollow, noarchive", + "Vary": "Authorization", + "Content-Type": "application/json" + }, + "body": { + "data": null, + "error": { + "code": "account_state_requires_review", + "message": "We could not safely determine your current hospital stay. Please ask your care team for help." + }, + "meta": { + "request_id": "ntg_req_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "generated_at": "2026-07-26T15:00:00Z", + "policy_version": "nightingale-encounter-access-policy.v0-candidate", + "authorization_evaluated_at": "2026-07-26T15:00:00Z", + "completeness": "withheld" + }, + "links": {} + } + }, + "rate_limited": { + "status": 429, + "headers": { + "Cache-Control": "private, no-store, max-age=0", + "Pragma": "no-cache", + "X-Robots-Tag": "noindex, nofollow, noarchive", + "Vary": "Authorization", + "Content-Type": "application/json" + }, + "body": { + "data": null, + "error": { + "code": "rate_limited", + "message": "Too many requests were submitted. Please try again later." + }, + "meta": { + "request_id": "ntg_req_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "generated_at": "2026-07-26T15:00:00Z", + "policy_version": "nightingale-encounter-access-policy.v0-candidate", + "authorization_evaluated_at": "2026-07-26T15:00:00Z", + "completeness": "withheld" + }, + "links": {} + } + }, + "temporarily_unavailable": { + "status": 503, + "headers": { + "Cache-Control": "private, no-store, max-age=0", + "Pragma": "no-cache", + "X-Robots-Tag": "noindex, nofollow, noarchive", + "Vary": "Authorization", + "Content-Type": "application/json" + }, + "body": { + "data": null, + "error": { + "code": "temporarily_unavailable", + "message": "Current hospital-stay information is temporarily unavailable. Please ask your care team if you need help now." + }, + "meta": { + "request_id": "ntg_req_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "generated_at": "2026-07-26T15:00:00Z", + "policy_version": "nightingale-encounter-access-policy.v0-candidate", + "authorization_evaluated_at": "2026-07-26T15:00:00Z", + "completeness": "withheld" + }, + "links": {} + } + } + }, + "cases": [ + { + "case_id": "eligible_self_single", + "category": "allow", + "preconditions": [ + "all release, identity, relationship, grant, source, handle, recheck, and audit gates pass", + "exactly one eligible self inpatient context exists" + ], + "expected_template": "success_one", + "audit_mode": "durable_evaluation_and_disclosure" + }, + { + "case_id": "no_candidate_grants", + "category": "omit", + "preconditions": [ + "all authoritative checks complete", + "no candidate grant exists for the principal" + ], + "expected_template": "success_empty", + "audit_mode": "durable_evaluation" + }, + { + "case_id": "revoked_grant_omitted", + "category": "omit", + "preconditions": ["only grant has status revoked"], + "expected_template": "success_empty", + "audit_mode": "durable_evaluation" + }, + { + "case_id": "suspended_grant_omitted", + "category": "omit", + "preconditions": ["only grant has status suspended"], + "expected_template": "success_empty", + "audit_mode": "durable_evaluation" + }, + { + "case_id": "pending_grant_omitted", + "category": "omit", + "preconditions": ["only grant has status pending"], + "expected_template": "success_empty", + "audit_mode": "durable_evaluation" + }, + { + "case_id": "closed_grant_omitted", + "category": "omit", + "preconditions": ["only grant has status closed"], + "expected_template": "success_empty", + "audit_mode": "durable_evaluation" + }, + { + "case_id": "expired_status_grant_omitted", + "category": "omit", + "preconditions": ["only grant has status expired"], + "expected_template": "success_empty", + "audit_mode": "durable_evaluation" + }, + { + "case_id": "revoked_timestamp_omitted", + "category": "omit", + "preconditions": [ + "grant status is active but revoked_at is non-null" + ], + "expected_template": "success_empty", + "audit_mode": "durable_evaluation" + }, + { + "case_id": "future_validity_omitted", + "category": "omit", + "preconditions": [ + "grant valid_from is later than authorization_evaluated_at" + ], + "expected_template": "success_empty", + "audit_mode": "durable_evaluation" + }, + { + "case_id": "expired_window_omitted", + "category": "omit", + "preconditions": [ + "grant expires_at is not later than authorization_evaluated_at" + ], + "expected_template": "success_empty", + "audit_mode": "durable_evaluation" + }, + { + "case_id": "wrong_principal_omitted", + "category": "nondisclosure", + "preconditions": ["candidate grant belongs to another principal"], + "expected_template": "success_empty", + "audit_mode": "durable_evaluation" + }, + { + "case_id": "purpose_mismatch_omitted", + "category": "omit", + "preconditions": [ + "grant has a recognized but non-inpatient purpose of use" + ], + "expected_template": "success_empty", + "audit_mode": "durable_evaluation" + }, + { + "case_id": "representative_relationship_held", + "category": "nondisclosure", + "preconditions": [ + "grant relationship is a recognized representative type", + "representative access is held for this candidate" + ], + "expected_template": "success_empty", + "audit_mode": "durable_evaluation" + }, + { + "case_id": "source_encounter_closed_omitted", + "category": "omit", + "preconditions": [ + "authoritative source confirms the encounter is no longer an active inpatient context" + ], + "expected_template": "success_empty", + "audit_mode": "durable_evaluation" + }, + { + "case_id": "product_disabled", + "category": "release-gate", + "preconditions": ["Nightingale product gate is false"], + "expected_template": "not_found", + "audit_mode": "best_effort_denial" + }, + { + "case_id": "operation_disabled", + "category": "release-gate", + "preconditions": ["encounter-access operation gate is false"], + "expected_template": "not_found", + "audit_mode": "best_effort_denial" + }, + { + "case_id": "authentication_missing", + "category": "identity", + "preconditions": ["no credential is present"], + "expected_template": "authentication_required", + "audit_mode": "best_effort_denial" + }, + { + "case_id": "wrong_identity_realm", + "category": "identity", + "preconditions": [ + "credential is valid for a non-Nightingale realm" + ], + "expected_template": "access_unavailable", + "audit_mode": "best_effort_denial" + }, + { + "case_id": "principal_inactive", + "category": "identity", + "preconditions": ["principal is not active"], + "expected_template": "access_unavailable", + "audit_mode": "best_effort_denial" + }, + { + "case_id": "principal_locked", + "category": "identity", + "preconditions": ["principal is locked"], + "expected_template": "access_unavailable", + "audit_mode": "best_effort_denial" + }, + { + "case_id": "session_missing", + "category": "session", + "preconditions": ["credential does not bind to an owned session"], + "expected_template": "authentication_required", + "audit_mode": "best_effort_denial" + }, + { + "case_id": "session_expired", + "category": "session", + "preconditions": ["session absolute or idle expiry is effective"], + "expected_template": "authentication_required", + "audit_mode": "best_effort_denial" + }, + { + "case_id": "session_revoked", + "category": "session", + "preconditions": ["session is revoked"], + "expected_template": "authentication_required", + "audit_mode": "best_effort_denial" + }, + { + "case_id": "identity_link_missing", + "category": "identity-integrity", + "preconditions": [ + "active principal has no current identity link for the grant" + ], + "expected_template": "account_state_requires_review", + "audit_mode": "durable_safety_failure" + }, + { + "case_id": "identity_link_revoked", + "category": "identity-integrity", + "preconditions": ["grant identity link is revoked"], + "expected_template": "account_state_requires_review", + "audit_mode": "durable_safety_failure" + }, + { + "case_id": "identity_link_merged", + "category": "identity-integrity", + "preconditions": ["grant identity link was merged or superseded"], + "expected_template": "account_state_requires_review", + "audit_mode": "durable_safety_failure" + }, + { + "case_id": "identity_principal_mismatch", + "category": "identity-integrity", + "preconditions": [ + "identity link and grant do not belong to the authenticated principal" + ], + "expected_template": "account_state_requires_review", + "audit_mode": "durable_safety_failure" + }, + { + "case_id": "unknown_relationship", + "category": "data-integrity", + "preconditions": [ + "grant relationship is not in the approved registry" + ], + "expected_template": "temporarily_unavailable", + "audit_mode": "durable_safety_failure" + }, + { + "case_id": "unknown_grant_status", + "category": "data-integrity", + "preconditions": ["grant status is not in the approved registry"], + "expected_template": "temporarily_unavailable", + "audit_mode": "durable_safety_failure" + }, + { + "case_id": "unknown_purpose_of_use", + "category": "data-integrity", + "preconditions": ["grant purpose is not in the approved registry"], + "expected_template": "temporarily_unavailable", + "audit_mode": "durable_safety_failure" + }, + { + "case_id": "malformed_nightingale_handle", + "category": "handle-integrity", + "preconditions": [ + "mapped Nightingale handle does not match the approved opaque format" + ], + "expected_template": "temporarily_unavailable", + "audit_mode": "durable_safety_failure" + }, + { + "case_id": "nightingale_handle_collision", + "category": "handle-integrity", + "preconditions": [ + "one Nightingale handle resolves to multiple authorization records" + ], + "expected_template": "temporarily_unavailable", + "audit_mode": "durable_safety_failure" + }, + { + "case_id": "source_link_missing", + "category": "source-integrity", + "preconditions": [ + "grant lacks an approved current source-encounter linkage" + ], + "expected_template": "account_state_requires_review", + "audit_mode": "durable_safety_failure" + }, + { + "case_id": "source_system_unavailable", + "category": "dependency", + "preconditions": [ + "current inpatient status cannot be authoritatively confirmed" + ], + "expected_template": "temporarily_unavailable", + "audit_mode": "best_effort_safety_failure" + }, + { + "case_id": "database_unavailable", + "category": "dependency", + "preconditions": ["authorization registry query does not complete"], + "expected_template": "temporarily_unavailable", + "audit_mode": "best_effort_safety_failure" + }, + { + "case_id": "request_audit_unavailable", + "category": "audit", + "preconditions": ["durable evaluation audit cannot be recorded"], + "expected_template": "temporarily_unavailable", + "audit_mode": "best_effort_safety_failure" + }, + { + "case_id": "disclosure_audit_unavailable", + "category": "audit", + "preconditions": [ + "durable handle-disclosure audit cannot be recorded" + ], + "expected_template": "temporarily_unavailable", + "audit_mode": "best_effort_safety_failure" + }, + { + "case_id": "multiple_eligible_contexts", + "category": "ambiguity", + "preconditions": [ + "more than one eligible self inpatient context remains after all gates" + ], + "expected_template": "account_state_requires_review", + "audit_mode": "durable_safety_failure" + }, + { + "case_id": "grant_changed_before_serialization", + "category": "race", + "preconditions": [ + "grant, relationship, identity, source, or handle version changes after evaluation and before serialization" + ], + "expected_template": "account_state_requires_review", + "audit_mode": "durable_safety_failure" + }, + { + "case_id": "policy_version_mismatch", + "category": "policy", + "preconditions": [ + "runtime policy identity differs from the approved contract policy" + ], + "expected_template": "temporarily_unavailable", + "audit_mode": "durable_safety_failure" + }, + { + "case_id": "malformed_scope_registry", + "category": "data-integrity", + "preconditions": [ + "grant capability registry contains an unknown or malformed authorization scope" + ], + "expected_template": "temporarily_unavailable", + "audit_mode": "durable_safety_failure" + }, + { + "case_id": "rate_limited", + "category": "ingress", + "preconditions": [ + "approved principal-specific rate limit is exceeded" + ], + "expected_template": "rate_limited", + "audit_mode": "best_effort_denial" + } + ] +} diff --git a/docs/nightingale/api-contract/candidates/patient-journeys/v0/candidate.json b/docs/nightingale/api-contract/candidates/patient-journeys/v0/candidate.json new file mode 100644 index 00000000..281278f8 --- /dev/null +++ b/docs/nightingale/api-contract/candidates/patient-journeys/v0/candidate.json @@ -0,0 +1,281 @@ +{ + "schema_version": 1, + "candidate_id": "nightingale.patient-journey-reference.v0-candidate", + "policy_version": "nightingale-patient-journey-reference.v0-candidate", + "reviewed_source_commit": "a825db89ec1efaf4b2c55a26e04d41161445b001", + "reviewed_at": "2026-07-27", + "status": "synthetic_reference_only_not_approved_for_implementation", + "purpose": "Define deterministic patient-journey safety and non-disclosure expectations before any Nightingale journey contract or runtime exists.", + "executable_boundary": { + "foundation_contract_path_count": 0, + "candidate_operation_count": 0, + "runtime_binding_count": 0, + "native_client_operation_count": 0, + "route_namespace_reserved_only": "/api/nightingale/v1" + }, + "global_constraints": { + "implementation_permitted": false, + "executable_contract_path_permitted": false, + "route_registration_permitted": false, + "controller_or_provider_binding_permitted": false, + "client_generation_permitted": false, + "native_networking_permitted": false, + "identity_provider_selection_permitted": false, + "source_adapter_selection_permitted": false, + "database_query_permitted": false, + "production_data_use_permitted": false, + "production_mutation_permitted": false, + "patient_or_representative_creation_permitted": false, + "clinical_content_release_permitted": false, + "communication_mutation_permitted": false, + "notification_delivery_permitted": false, + "offline_phi_storage_permitted": false, + "migration_execution_permitted": false, + "deployment_permitted": false, + "pilot_activation_permitted": false + }, + "approval_gates": [ + "named-product-and-contract-owner", + "approved-self-identity-session-and-recovery-design", + "approved-current-inpatient-source-and-lifecycle-design", + "approved-operation-specific-authorization-and-nondisclosure-matrix", + "approved-field-source-release-freshness-uncertainty-correction-language-and-offline-matrix", + "approved-clinical-content-and-patient-language", + "approved-communication-routing-replay-urgency-and-patient-visible-state-model", + "approved-representative-consent-scope-expiry-revocation-and-sensitive-service-policy", + "approved-accessibility-language-interpreter-and-accommodation-design", + "approved-privacy-security-threat-and-clinical-hazard-controls", + "approved-audit-monitoring-support-incident-rollback-and-kill-switch-plan", + "canonical-backend-ios-android-fixture-parity", + "default-off-nonproduction-integration-approval" + ], + "required_surfaces": [ + "today", + "my-path", + "care-team", + "communication" + ], + "disclosure_modes": [ + "released-empty-or-not-available", + "correction-replacement-or-not-available", + "released-fields-only", + "released-correction", + "generic-withhold", + "communication-state-only", + "correction-replacement", + "accessibility-equivalent", + "not-available", + "approved-stale-or-not-available" + ], + "family_count": 15, + "case_count": 27, + "families": [ + { + "family_id": "admission", + "required_case_ids": [ + "adult_admission_context_established" + ] + }, + { + "family_id": "unit-transfer", + "required_case_ids": [ + "unit_transfer_old_location_withdrawn" + ] + }, + { + "family_id": "procedure", + "required_case_ids": [ + "procedure_plan_released_without_outcome_inference" + ] + }, + { + "family_id": "ancillary-test-and-result", + "required_case_ids": [ + "ancillary_test_delay_released", + "ancillary_result_not_released", + "ancillary_result_separately_released" + ] + }, + { + "family_id": "pre-round-question", + "required_case_ids": [ + "pre_round_question_server_accepted", + "pre_round_response_separately_released" + ] + }, + { + "family_id": "shift-handoff", + "required_case_ids": [ + "shift_handoff_preserves_thread_accountability" + ] + }, + { + "family_id": "discharge-estimate", + "required_case_ids": [ + "discharge_estimate_corrected" + ] + }, + { + "family_id": "discharge-with-open-thread", + "required_case_ids": [ + "discharge_closes_open_thread" + ] + }, + { + "family_id": "identity-correction", + "required_case_ids": [ + "identity_correction_revokes_old_context" + ] + }, + { + "family_id": "representative-lifecycle", + "required_case_ids": [ + "representative_invited_without_access", + "representative_scoped_candidate", + "representative_scope_expired", + "representative_scope_revoked" + ] + }, + { + "family_id": "language-and-interpreter", + "required_case_ids": [ + "limited_english_interpreter_support" + ] + }, + { + "family_id": "accommodations", + "required_case_ids": [ + "visual_accommodation_equivalence", + "hearing_accommodation_equivalence", + "motor_accommodation_equivalence", + "cognitive_accommodation_equivalence", + "low_literacy_teach_back_equivalence" + ] + }, + { + "family_id": "sensitive-data", + "required_case_ids": [ + "sensitive_data_fails_closed" + ] + }, + { + "family_id": "source-outage-and-staleness", + "required_case_ids": [ + "source_outage_sections_not_available", + "stale_projection_policy_evaluated_per_field" + ] + }, + { + "family_id": "content-retraction-and-correction", + "required_case_ids": [ + "incorrect_content_retracted", + "incorrect_content_corrected_replacement" + ] + } + ], + "required_case_ids": [ + "adult_admission_context_established", + "unit_transfer_old_location_withdrawn", + "procedure_plan_released_without_outcome_inference", + "ancillary_test_delay_released", + "ancillary_result_not_released", + "ancillary_result_separately_released", + "pre_round_question_server_accepted", + "pre_round_response_separately_released", + "shift_handoff_preserves_thread_accountability", + "discharge_estimate_corrected", + "discharge_closes_open_thread", + "identity_correction_revokes_old_context", + "representative_invited_without_access", + "representative_scoped_candidate", + "representative_scope_expired", + "representative_scope_revoked", + "limited_english_interpreter_support", + "visual_accommodation_equivalence", + "hearing_accommodation_equivalence", + "motor_accommodation_equivalence", + "cognitive_accommodation_equivalence", + "low_literacy_teach_back_equivalence", + "sensitive_data_fails_closed", + "source_outage_sections_not_available", + "stale_projection_policy_evaluated_per_field", + "incorrect_content_retracted", + "incorrect_content_corrected_replacement" + ], + "required_findings": { + "generated_artifacts_match_builder": true, + "every_fixture_is_synthetic_and_contains_no_phi": true, + "every_case_is_held_and_non_runnable": true, + "every_case_requires_all_global_approval_gates": true, + "every_disclosed_value_requires_separate_release": true, + "every_response_is_atomic_and_no_store": true, + "unavailable_and_released_empty_are_distinct": true, + "source_fact_does_not_equal_patient_release": true, + "server_acceptance_does_not_equal_staff_delivery_or_review": true, + "notification_delivery_is_not_available": true, + "representative_access_remains_held": true, + "sensitive_service_requests_fail_closed_without_existence_disclosure": true, + "identity_correction_revokes_old_handles_and_requires_reproof": true, + "source_outage_never_presents_last_known_value_as_current": true, + "stale_display_requires_field_specific_approval_and_notice": true, + "correction_replaces_and_retraction_withholds": true, + "accessibility_changes_never_remove_information_or_actions": true, + "no_offline_phi_or_queued_mutation_is_permitted": true, + "production_data_was_not_used": true + }, + "evidence": { + "source_count": 12, + "source_inventory_digest": "c380eda3c774a9c8a0aaae63f1ab368aa2ed90acc3c6f7130bdd663f4f97546c", + "sources": [ + { + "path": "docs/nightingale/api-contract/nightingale-foundation.v0.json", + "sha256": "6765da41f884cec3cb6596e2484a47297878940731b306a1b5ecf664cf7c0580" + }, + { + "path": "docs/nightingale/api-contract/candidates/encounter-access/v0/candidate.json", + "sha256": "6d3b587d80b5139e7ae64db1fb3f7a273f4991efd06ed13da0e2f7959ea2983b" + }, + { + "path": "docs/nightingale/api-contract/candidates/encounter-access/v0/fixtures.json", + "sha256": "968373aba829b10259d8c40a107f11646a98ebd2f5d484ec695e3e424f7af157" + }, + { + "path": "docs/nightingale/api-contract/candidates/today/v0/candidate.json", + "sha256": "4f272e90fd306f878a1bcad45c8a46ff1c7aa2e743f3dfc99e53089fa1bef012" + }, + { + "path": "docs/nightingale/api-contract/candidates/today/v0/fixtures.json", + "sha256": "9efdc028ec9a6cf547881d73b6e65c69d633daf63b09ec594d1a9b52f6b9d72e" + }, + { + "path": "docs/nightingale/identity/candidates/v0/candidate.json", + "sha256": "8729d9cd7e3cc742678ef61768570349ab8c0fe9610bacd66c13ad5318dc5daa" + }, + { + "path": "docs/nightingale/identity/candidates/v0/fixtures.json", + "sha256": "8fb4fdb5789b2a35225ff3a7fa02a26d3c33e155baa859ed8a6f21b319d456e7" + }, + { + "path": "docs/nightingale/source-candidates/current-inpatient/v0/candidate.json", + "sha256": "214a18cfdff6661ba6da3c29dbde74ba4299999087c219da38004cd291c3cf67" + }, + { + "path": "docs/nightingale/source-candidates/current-inpatient/v0/fixtures.json", + "sha256": "61acdc5b273090023eb7c077df0bf737bb20bef658d001fb4b6ef1a279cb7240" + }, + { + "path": "docs/nightingale/migration/candidates/v0/communication-notification-source-classification.json", + "sha256": "82ae05cbe7c24167ba5a7f26b16ea0b79d74d11d2041a63a474c729dfc5b1bf8" + }, + { + "path": "docs/nightingale/migration/candidates/v0/journey-preference-presentation-release-source-classification.json", + "sha256": "b24612869036dd1ee208dd1629a35e58bb106433c59f51c5bf31b3856485ff0f" + }, + { + "path": "docs/hummingbird/ZEPHYRUS-HUMMINGBIRD-FUNCTIONAL-PARITY-AND-PATIENT-EXPERIENCE-PLAN-2026-07-19.md", + "sha256": "5b3f15dbc944e9890070cf3eae3fca31b0a4e68b84d0174e09497e55d3276246" + } + ], + "fixtures_sha256": "525c6f9264a14687b906485fcd105dd689c41e207785355ebf4f31a38f569de9" + } +} diff --git a/docs/nightingale/api-contract/candidates/patient-journeys/v0/fixtures.json b/docs/nightingale/api-contract/candidates/patient-journeys/v0/fixtures.json new file mode 100644 index 00000000..9c9cfbec --- /dev/null +++ b/docs/nightingale/api-contract/candidates/patient-journeys/v0/fixtures.json @@ -0,0 +1,2491 @@ +{ + "schema_version": 1, + "candidate_id": "nightingale.patient-journey-reference.v0-candidate", + "policy_version": "nightingale-patient-journey-reference.v0-candidate", + "fixed_clock": "2026-07-27T17:00:00Z", + "fixture_class": "synthetic-no-phi", + "case_count": 27, + "cases": [ + { + "case_id": "adult_admission_context_established", + "family_id": "admission", + "fixture_class": "synthetic-no-phi", + "execution_status": "held-not-runnable", + "actor": "self", + "encounter_state": "current-inpatient", + "preconditions": [ + "self-identity-and-session-approved", + "authoritative-current-inpatient-context-established", + "no-patient-facing-projection-yet-released" + ], + "event": "admission-context-becomes-current", + "expected": { + "authorization_result": "eligible-only-after-every-required-gate", + "disclosure": { + "mode": "released-empty-or-not-available", + "surfaces": [ + "today", + "my-path", + "care-team" + ], + "patient_message_key": "journey.admission.waiting_for_released_information", + "values_must_be_separately_released": true, + "atomic_response_required": true, + "no_store_required": true + }, + "state_transitions": [ + "encounter-access-empty-to-one-current-context", + "each-unreleased-surface-remains-not-available", + "released-empty-never-means-no-care-is-planned" + ], + "communication": { + "compose_state": "not-applicable", + "accepted_state_meaning": "not-applicable", + "routing_state": "not-applicable", + "notification_state": "not-available", + "volatile_draft_action": "clear-on-inactive-or-context-loss" + }, + "nondisclosure": [ + "no-source-system-identifier", + "no-patient-principal-grant-encounter-or-staff-identifier", + "no-unreleased-clinical-value", + "no-withheld-resource-existence-or-denial-reason", + "no-internal-routing-pool-or-staffing-detail", + "no-approval-actor-or-internal-correction-target" + ], + "prohibited_inferences": [ + "admission-context-implies-diagnosis", + "absence-of-projection-implies-absence-of-plan" + ], + "accessibility": { + "profile": "same-information-and-actions-through-supported-presentation", + "essential_information_in_imagery_permitted": false, + "color_only_state_permitted": false, + "patient_action_loss_permitted": false + }, + "audit": [ + "journey-evaluation-with-policy-and-case-id", + "authorization-and-current-context-outcome", + "disclosed-field-keys-without-values", + "communication-state-transition-without-message-body", + "withhold-correction-retraction-or-outage-reason-code-without-clinical-content" + ], + "offline": { + "phi_cache_permitted": false, + "stale_value_may_be_presented_as_current": false, + "queued_mutation_permitted": false, + "last_known_value_requires_separate_approved_policy": true + }, + "rollback": { + "kill_switch_required": true, + "release_withdrawal_required": true, + "cached_handle_and_volatile_draft_purge_required": true + } + }, + "required_approval_gates": [ + "named-product-and-contract-owner", + "approved-self-identity-session-and-recovery-design", + "approved-current-inpatient-source-and-lifecycle-design", + "approved-operation-specific-authorization-and-nondisclosure-matrix", + "approved-field-source-release-freshness-uncertainty-correction-language-and-offline-matrix", + "approved-clinical-content-and-patient-language", + "approved-communication-routing-replay-urgency-and-patient-visible-state-model", + "approved-representative-consent-scope-expiry-revocation-and-sensitive-service-policy", + "approved-accessibility-language-interpreter-and-accommodation-design", + "approved-privacy-security-threat-and-clinical-hazard-controls", + "approved-audit-monitoring-support-incident-rollback-and-kill-switch-plan", + "canonical-backend-ios-android-fixture-parity", + "default-off-nonproduction-integration-approval", + "approved-admission-context-and-first-patient-release-policy" + ] + }, + { + "case_id": "unit_transfer_old_location_withdrawn", + "family_id": "unit-transfer", + "fixture_class": "synthetic-no-phi", + "execution_status": "held-not-runnable", + "actor": "self", + "encounter_state": "current-inpatient", + "preconditions": [ + "one-current-inpatient-context", + "old-care-location-was-released", + "authoritative-transfer-event-reconciled" + ], + "event": "intra-hospital-unit-transfer", + "expected": { + "authorization_result": "eligible-only-after-every-required-gate", + "disclosure": { + "mode": "correction-replacement-or-not-available", + "surfaces": [ + "today", + "care-team", + "communication" + ], + "patient_message_key": "journey.transfer.location_updating", + "values_must_be_separately_released": true, + "atomic_response_required": true, + "no_store_required": true + }, + "state_transitions": [ + "old-location-withdrawn-before-new-location-disclosure", + "new-location-shown-only-after-separate-release", + "open-accountable-work-rerouted-without-copying-message-content" + ], + "communication": { + "compose_state": "held-until-current-context-and-routing-revalidated", + "accepted_state_meaning": "server-acceptance-only", + "routing_state": "content-free-reroute-or-unresolved", + "notification_state": "not-available", + "volatile_draft_action": "preserve-only-if-same-authorized-context" + }, + "nondisclosure": [ + "no-source-system-identifier", + "no-patient-principal-grant-encounter-or-staff-identifier", + "no-unreleased-clinical-value", + "no-withheld-resource-existence-or-denial-reason", + "no-internal-routing-pool-or-staffing-detail", + "no-approval-actor-or-internal-correction-target" + ], + "prohibited_inferences": [ + "transfer-implies-clinical-condition", + "routing-change-implies-a-specific-staff-member-read-the-message" + ], + "accessibility": { + "profile": "same-information-and-actions-through-supported-presentation", + "essential_information_in_imagery_permitted": false, + "color_only_state_permitted": false, + "patient_action_loss_permitted": false + }, + "audit": [ + "journey-evaluation-with-policy-and-case-id", + "authorization-and-current-context-outcome", + "disclosed-field-keys-without-values", + "communication-state-transition-without-message-body", + "withhold-correction-retraction-or-outage-reason-code-without-clinical-content" + ], + "offline": { + "phi_cache_permitted": false, + "stale_value_may_be_presented_as_current": false, + "queued_mutation_permitted": false, + "last_known_value_requires_separate_approved_policy": true + }, + "rollback": { + "kill_switch_required": true, + "release_withdrawal_required": true, + "cached_handle_and_volatile_draft_purge_required": true + } + }, + "required_approval_gates": [ + "named-product-and-contract-owner", + "approved-self-identity-session-and-recovery-design", + "approved-current-inpatient-source-and-lifecycle-design", + "approved-operation-specific-authorization-and-nondisclosure-matrix", + "approved-field-source-release-freshness-uncertainty-correction-language-and-offline-matrix", + "approved-clinical-content-and-patient-language", + "approved-communication-routing-replay-urgency-and-patient-visible-state-model", + "approved-representative-consent-scope-expiry-revocation-and-sensitive-service-policy", + "approved-accessibility-language-interpreter-and-accommodation-design", + "approved-privacy-security-threat-and-clinical-hazard-controls", + "approved-audit-monitoring-support-incident-rollback-and-kill-switch-plan", + "canonical-backend-ios-android-fixture-parity", + "default-off-nonproduction-integration-approval", + "approved-transfer-source-and-current-location-release-policy", + "approved-transfer-communication-reroute-policy" + ] + }, + { + "case_id": "procedure_plan_released_without_outcome_inference", + "family_id": "procedure", + "fixture_class": "synthetic-no-phi", + "execution_status": "held-not-runnable", + "actor": "self", + "encounter_state": "current-inpatient", + "preconditions": [ + "procedure-plan-field-separately-released", + "timing-confidence-and-can-change-state-present", + "no-procedure-outcome-released" + ], + "event": "procedure-during-admission", + "expected": { + "authorization_result": "eligible-only-after-every-required-gate", + "disclosure": { + "mode": "released-fields-only", + "surfaces": [ + "today", + "my-path" + ], + "patient_message_key": "journey.procedure.released_plan_only", + "values_must_be_separately_released": true, + "atomic_response_required": true, + "no_store_required": true + }, + "state_transitions": [ + "planned-to-confirmed-only-from-authoritative-released-state", + "in-progress-or-completed-never-inferred-from-clock-time", + "outcome-remains-absent-until-separately-released" + ], + "communication": { + "compose_state": "not-applicable", + "accepted_state_meaning": "not-applicable", + "routing_state": "not-applicable", + "notification_state": "not-available", + "volatile_draft_action": "clear-on-inactive-or-context-loss" + }, + "nondisclosure": [ + "no-source-system-identifier", + "no-patient-principal-grant-encounter-or-staff-identifier", + "no-unreleased-clinical-value", + "no-withheld-resource-existence-or-denial-reason", + "no-internal-routing-pool-or-staffing-detail", + "no-approval-actor-or-internal-correction-target" + ], + "prohibited_inferences": [ + "scheduled-time-implies-procedure-started", + "procedure-completed-implies-result-or-outcome" + ], + "accessibility": { + "profile": "same-information-and-actions-through-supported-presentation", + "essential_information_in_imagery_permitted": false, + "color_only_state_permitted": false, + "patient_action_loss_permitted": false + }, + "audit": [ + "journey-evaluation-with-policy-and-case-id", + "authorization-and-current-context-outcome", + "disclosed-field-keys-without-values", + "communication-state-transition-without-message-body", + "withhold-correction-retraction-or-outage-reason-code-without-clinical-content" + ], + "offline": { + "phi_cache_permitted": false, + "stale_value_may_be_presented_as_current": false, + "queued_mutation_permitted": false, + "last_known_value_requires_separate_approved_policy": true + }, + "rollback": { + "kill_switch_required": true, + "release_withdrawal_required": true, + "cached_handle_and_volatile_draft_purge_required": true + } + }, + "required_approval_gates": [ + "named-product-and-contract-owner", + "approved-self-identity-session-and-recovery-design", + "approved-current-inpatient-source-and-lifecycle-design", + "approved-operation-specific-authorization-and-nondisclosure-matrix", + "approved-field-source-release-freshness-uncertainty-correction-language-and-offline-matrix", + "approved-clinical-content-and-patient-language", + "approved-communication-routing-replay-urgency-and-patient-visible-state-model", + "approved-representative-consent-scope-expiry-revocation-and-sensitive-service-policy", + "approved-accessibility-language-interpreter-and-accommodation-design", + "approved-privacy-security-threat-and-clinical-hazard-controls", + "approved-audit-monitoring-support-incident-rollback-and-kill-switch-plan", + "canonical-backend-ios-android-fixture-parity", + "default-off-nonproduction-integration-approval", + "approved-procedure-vocabulary-timing-and-content-release-policy" + ] + }, + { + "case_id": "ancillary_test_delay_released", + "family_id": "ancillary-test-and-result", + "fixture_class": "synthetic-no-phi", + "execution_status": "held-not-runnable", + "actor": "self", + "encounter_state": "current-inpatient", + "preconditions": [ + "test-plan-was-released", + "authoritative-delay-state-reconciled", + "delay-copy-and-uncertainty-level-approved" + ], + "event": "ancillary-test-delayed", + "expected": { + "authorization_result": "eligible-only-after-every-required-gate", + "disclosure": { + "mode": "released-correction", + "surfaces": [ + "today", + "my-path" + ], + "patient_message_key": "journey.test.delay_released", + "values_must_be_separately_released": true, + "atomic_response_required": true, + "no_store_required": true + }, + "state_transitions": [ + "prior-timing-withdrawn", + "delayed-state-and-updated-window-shown-only-if-released", + "no-cause-or-blame-disclosed-without-separate-release" + ], + "communication": { + "compose_state": "not-applicable", + "accepted_state_meaning": "not-applicable", + "routing_state": "not-applicable", + "notification_state": "not-available", + "volatile_draft_action": "clear-on-inactive-or-context-loss" + }, + "nondisclosure": [ + "no-source-system-identifier", + "no-patient-principal-grant-encounter-or-staff-identifier", + "no-unreleased-clinical-value", + "no-withheld-resource-existence-or-denial-reason", + "no-internal-routing-pool-or-staffing-detail", + "no-approval-actor-or-internal-correction-target" + ], + "prohibited_inferences": [ + "delay-implies-cancellation", + "delay-copy-identifies-staff-or-operational-cause" + ], + "accessibility": { + "profile": "same-information-and-actions-through-supported-presentation", + "essential_information_in_imagery_permitted": false, + "color_only_state_permitted": false, + "patient_action_loss_permitted": false + }, + "audit": [ + "journey-evaluation-with-policy-and-case-id", + "authorization-and-current-context-outcome", + "disclosed-field-keys-without-values", + "communication-state-transition-without-message-body", + "withhold-correction-retraction-or-outage-reason-code-without-clinical-content" + ], + "offline": { + "phi_cache_permitted": false, + "stale_value_may_be_presented_as_current": false, + "queued_mutation_permitted": false, + "last_known_value_requires_separate_approved_policy": true + }, + "rollback": { + "kill_switch_required": true, + "release_withdrawal_required": true, + "cached_handle_and_volatile_draft_purge_required": true + } + }, + "required_approval_gates": [ + "named-product-and-contract-owner", + "approved-self-identity-session-and-recovery-design", + "approved-current-inpatient-source-and-lifecycle-design", + "approved-operation-specific-authorization-and-nondisclosure-matrix", + "approved-field-source-release-freshness-uncertainty-correction-language-and-offline-matrix", + "approved-clinical-content-and-patient-language", + "approved-communication-routing-replay-urgency-and-patient-visible-state-model", + "approved-representative-consent-scope-expiry-revocation-and-sensitive-service-policy", + "approved-accessibility-language-interpreter-and-accommodation-design", + "approved-privacy-security-threat-and-clinical-hazard-controls", + "approved-audit-monitoring-support-incident-rollback-and-kill-switch-plan", + "canonical-backend-ios-android-fixture-parity", + "default-off-nonproduction-integration-approval", + "approved-test-delay-source-vocabulary-and-uncertainty-policy" + ] + }, + { + "case_id": "ancillary_result_not_released", + "family_id": "ancillary-test-and-result", + "fixture_class": "synthetic-no-phi", + "execution_status": "held-not-runnable", + "actor": "self", + "encounter_state": "current-inpatient", + "preconditions": [ + "test-completion-may-exist-in-source", + "patient-result-release-is-absent-pending-or-withheld" + ], + "event": "result-source-fact-arrives-without-patient-release", + "expected": { + "authorization_result": "indistinguishable-withhold", + "disclosure": { + "mode": "generic-withhold", + "surfaces": [ + "today", + "my-path" + ], + "patient_message_key": "journey.result.not_available", + "values_must_be_separately_released": true, + "atomic_response_required": true, + "no_store_required": true + }, + "state_transitions": [ + "source-fact-does-not-create-patient-release", + "prior-section-remains-not-available-or-released-empty" + ], + "communication": { + "compose_state": "not-applicable", + "accepted_state_meaning": "not-applicable", + "routing_state": "not-applicable", + "notification_state": "not-available", + "volatile_draft_action": "clear-on-inactive-or-context-loss" + }, + "nondisclosure": [ + "no-source-system-identifier", + "no-patient-principal-grant-encounter-or-staff-identifier", + "no-unreleased-clinical-value", + "no-withheld-resource-existence-or-denial-reason", + "no-internal-routing-pool-or-staffing-detail", + "no-approval-actor-or-internal-correction-target" + ], + "prohibited_inferences": [ + "result-exists", + "result-is-normal-abnormal-final-or-reviewed" + ], + "accessibility": { + "profile": "same-information-and-actions-through-supported-presentation", + "essential_information_in_imagery_permitted": false, + "color_only_state_permitted": false, + "patient_action_loss_permitted": false + }, + "audit": [ + "journey-evaluation-with-policy-and-case-id", + "authorization-and-current-context-outcome", + "disclosed-field-keys-without-values", + "communication-state-transition-without-message-body", + "withhold-correction-retraction-or-outage-reason-code-without-clinical-content" + ], + "offline": { + "phi_cache_permitted": false, + "stale_value_may_be_presented_as_current": false, + "queued_mutation_permitted": false, + "last_known_value_requires_separate_approved_policy": true + }, + "rollback": { + "kill_switch_required": true, + "release_withdrawal_required": true, + "cached_handle_and_volatile_draft_purge_required": true + } + }, + "required_approval_gates": [ + "named-product-and-contract-owner", + "approved-self-identity-session-and-recovery-design", + "approved-current-inpatient-source-and-lifecycle-design", + "approved-operation-specific-authorization-and-nondisclosure-matrix", + "approved-field-source-release-freshness-uncertainty-correction-language-and-offline-matrix", + "approved-clinical-content-and-patient-language", + "approved-communication-routing-replay-urgency-and-patient-visible-state-model", + "approved-representative-consent-scope-expiry-revocation-and-sensitive-service-policy", + "approved-accessibility-language-interpreter-and-accommodation-design", + "approved-privacy-security-threat-and-clinical-hazard-controls", + "approved-audit-monitoring-support-incident-rollback-and-kill-switch-plan", + "canonical-backend-ios-android-fixture-parity", + "default-off-nonproduction-integration-approval", + "approved-result-release-and-sensitive-content-policy" + ] + }, + { + "case_id": "ancillary_result_separately_released", + "family_id": "ancillary-test-and-result", + "fixture_class": "synthetic-no-phi", + "execution_status": "held-not-runnable", + "actor": "self", + "encounter_state": "current-inpatient", + "preconditions": [ + "result-field-separately-approved-and-released", + "review-language-freshness-and-correction-context-present" + ], + "event": "patient-result-release-becomes-effective", + "expected": { + "authorization_result": "eligible-only-after-every-required-gate", + "disclosure": { + "mode": "released-fields-only", + "surfaces": [ + "my-path" + ], + "patient_message_key": "journey.result.released", + "values_must_be_separately_released": true, + "atomic_response_required": true, + "no_store_required": true + }, + "state_transitions": [ + "not-available-to-released-only-at-effective-release", + "replacement-release-supersedes-prior-released-value" + ], + "communication": { + "compose_state": "not-applicable", + "accepted_state_meaning": "not-applicable", + "routing_state": "not-applicable", + "notification_state": "not-available", + "volatile_draft_action": "clear-on-inactive-or-context-loss" + }, + "nondisclosure": [ + "no-source-system-identifier", + "no-patient-principal-grant-encounter-or-staff-identifier", + "no-unreleased-clinical-value", + "no-withheld-resource-existence-or-denial-reason", + "no-internal-routing-pool-or-staffing-detail", + "no-approval-actor-or-internal-correction-target" + ], + "prohibited_inferences": [ + "released-result-implies-clinical-interpretation", + "display-implies-care-team-discussion-occurred" + ], + "accessibility": { + "profile": "same-information-and-actions-through-supported-presentation", + "essential_information_in_imagery_permitted": false, + "color_only_state_permitted": false, + "patient_action_loss_permitted": false + }, + "audit": [ + "journey-evaluation-with-policy-and-case-id", + "authorization-and-current-context-outcome", + "disclosed-field-keys-without-values", + "communication-state-transition-without-message-body", + "withhold-correction-retraction-or-outage-reason-code-without-clinical-content" + ], + "offline": { + "phi_cache_permitted": false, + "stale_value_may_be_presented_as_current": false, + "queued_mutation_permitted": false, + "last_known_value_requires_separate_approved_policy": true + }, + "rollback": { + "kill_switch_required": true, + "release_withdrawal_required": true, + "cached_handle_and_volatile_draft_purge_required": true + } + }, + "required_approval_gates": [ + "named-product-and-contract-owner", + "approved-self-identity-session-and-recovery-design", + "approved-current-inpatient-source-and-lifecycle-design", + "approved-operation-specific-authorization-and-nondisclosure-matrix", + "approved-field-source-release-freshness-uncertainty-correction-language-and-offline-matrix", + "approved-clinical-content-and-patient-language", + "approved-communication-routing-replay-urgency-and-patient-visible-state-model", + "approved-representative-consent-scope-expiry-revocation-and-sensitive-service-policy", + "approved-accessibility-language-interpreter-and-accommodation-design", + "approved-privacy-security-threat-and-clinical-hazard-controls", + "approved-audit-monitoring-support-incident-rollback-and-kill-switch-plan", + "canonical-backend-ios-android-fixture-parity", + "default-off-nonproduction-integration-approval", + "approved-result-release-interpretation-and-follow-up-language" + ] + }, + { + "case_id": "pre_round_question_server_accepted", + "family_id": "pre-round-question", + "fixture_class": "synthetic-no-phi", + "execution_status": "held-not-runnable", + "actor": "self", + "encounter_state": "current-inpatient", + "preconditions": [ + "communication-operation-approved-and-enabled", + "current-self-context-and-capability-revalidated", + "accountable-routing-consumer-ready", + "exact-idempotency-and-client-message-identities-present" + ], + "event": "patient-submits-pre-round-question", + "expected": { + "authorization_result": "eligible-only-after-every-required-gate", + "disclosure": { + "mode": "communication-state-only", + "surfaces": [ + "today", + "care-team", + "communication" + ], + "patient_message_key": "journey.question.server_accepted", + "values_must_be_separately_released": true, + "atomic_response_required": true, + "no_store_required": true + }, + "state_transitions": [ + "draft-to-server-accepted", + "server-accepted-does-not-equal-staff-delivery-or-review", + "content-free-accountable-routing-fact-appended" + ], + "communication": { + "compose_state": "available-only-after-all-gates", + "accepted_state_meaning": "durably-accepted-by-server", + "routing_state": "pending-accountable-staff-projection", + "notification_state": "not-available", + "volatile_draft_action": "clear-only-after-exact-accepted-response" + }, + "nondisclosure": [ + "no-source-system-identifier", + "no-patient-principal-grant-encounter-or-staff-identifier", + "no-unreleased-clinical-value", + "no-withheld-resource-existence-or-denial-reason", + "no-internal-routing-pool-or-staffing-detail", + "no-approval-actor-or-internal-correction-target" + ], + "prohibited_inferences": [ + "care-team-received-message", + "specific-response-time", + "urgent-monitoring-or-emergency-service" + ], + "accessibility": { + "profile": "same-information-and-actions-through-supported-presentation", + "essential_information_in_imagery_permitted": false, + "color_only_state_permitted": false, + "patient_action_loss_permitted": false + }, + "audit": [ + "journey-evaluation-with-policy-and-case-id", + "authorization-and-current-context-outcome", + "disclosed-field-keys-without-values", + "communication-state-transition-without-message-body", + "withhold-correction-retraction-or-outage-reason-code-without-clinical-content" + ], + "offline": { + "phi_cache_permitted": false, + "stale_value_may_be_presented_as_current": false, + "queued_mutation_permitted": false, + "last_known_value_requires_separate_approved_policy": true + }, + "rollback": { + "kill_switch_required": true, + "release_withdrawal_required": true, + "cached_handle_and_volatile_draft_purge_required": true + } + }, + "required_approval_gates": [ + "named-product-and-contract-owner", + "approved-self-identity-session-and-recovery-design", + "approved-current-inpatient-source-and-lifecycle-design", + "approved-operation-specific-authorization-and-nondisclosure-matrix", + "approved-field-source-release-freshness-uncertainty-correction-language-and-offline-matrix", + "approved-clinical-content-and-patient-language", + "approved-communication-routing-replay-urgency-and-patient-visible-state-model", + "approved-representative-consent-scope-expiry-revocation-and-sensitive-service-policy", + "approved-accessibility-language-interpreter-and-accommodation-design", + "approved-privacy-security-threat-and-clinical-hazard-controls", + "approved-audit-monitoring-support-incident-rollback-and-kill-switch-plan", + "canonical-backend-ios-android-fixture-parity", + "default-off-nonproduction-integration-approval", + "approved-pre-round-topic-and-urgent-help-copy", + "approved-idempotent-retry-and-ambiguous-result-recovery" + ] + }, + { + "case_id": "pre_round_response_separately_released", + "family_id": "pre-round-question", + "fixture_class": "synthetic-no-phi", + "execution_status": "held-not-runnable", + "actor": "self", + "encounter_state": "current-inpatient", + "preconditions": [ + "prior-question-server-accepted", + "accountable-staff-response-exists", + "response-content-separately-approved-for-patient-release" + ], + "event": "approved-summary-response-released", + "expected": { + "authorization_result": "eligible-only-after-every-required-gate", + "disclosure": { + "mode": "released-fields-only", + "surfaces": [ + "communication", + "today" + ], + "patient_message_key": "journey.question.response_released", + "values_must_be_separately_released": true, + "atomic_response_required": true, + "no_store_required": true + }, + "state_transitions": [ + "accepted-question-remains-immutable", + "staff-response-is-distinct-released-message", + "read-state-never-inferred-from-delivery-state" + ], + "communication": { + "compose_state": "depends-on-current-thread-capability", + "accepted_state_meaning": "prior-question-durably-accepted", + "routing_state": "accountable-response-released", + "notification_state": "held-until-provider-and-payload-approval", + "volatile_draft_action": "clear-only-after-exact-accepted-response" + }, + "nondisclosure": [ + "no-source-system-identifier", + "no-patient-principal-grant-encounter-or-staff-identifier", + "no-unreleased-clinical-value", + "no-withheld-resource-existence-or-denial-reason", + "no-internal-routing-pool-or-staffing-detail", + "no-approval-actor-or-internal-correction-target" + ], + "prohibited_inferences": [ + "response-is-a-complete-clinical-summary", + "notification-was-delivered" + ], + "accessibility": { + "profile": "same-information-and-actions-through-supported-presentation", + "essential_information_in_imagery_permitted": false, + "color_only_state_permitted": false, + "patient_action_loss_permitted": false + }, + "audit": [ + "journey-evaluation-with-policy-and-case-id", + "authorization-and-current-context-outcome", + "disclosed-field-keys-without-values", + "communication-state-transition-without-message-body", + "withhold-correction-retraction-or-outage-reason-code-without-clinical-content" + ], + "offline": { + "phi_cache_permitted": false, + "stale_value_may_be_presented_as_current": false, + "queued_mutation_permitted": false, + "last_known_value_requires_separate_approved_policy": true + }, + "rollback": { + "kill_switch_required": true, + "release_withdrawal_required": true, + "cached_handle_and_volatile_draft_purge_required": true + } + }, + "required_approval_gates": [ + "named-product-and-contract-owner", + "approved-self-identity-session-and-recovery-design", + "approved-current-inpatient-source-and-lifecycle-design", + "approved-operation-specific-authorization-and-nondisclosure-matrix", + "approved-field-source-release-freshness-uncertainty-correction-language-and-offline-matrix", + "approved-clinical-content-and-patient-language", + "approved-communication-routing-replay-urgency-and-patient-visible-state-model", + "approved-representative-consent-scope-expiry-revocation-and-sensitive-service-policy", + "approved-accessibility-language-interpreter-and-accommodation-design", + "approved-privacy-security-threat-and-clinical-hazard-controls", + "approved-audit-monitoring-support-incident-rollback-and-kill-switch-plan", + "canonical-backend-ios-android-fixture-parity", + "default-off-nonproduction-integration-approval", + "approved-staff-response-release-and-patient-language-policy" + ] + }, + { + "case_id": "shift_handoff_preserves_thread_accountability", + "family_id": "shift-handoff", + "fixture_class": "synthetic-no-phi", + "execution_status": "held-not-runnable", + "actor": "self", + "encounter_state": "current-inpatient", + "preconditions": [ + "open-thread-with-server-accepted-message", + "current-responsibility-membership-changes", + "eligible-destination-resolves-or-fails-unresolved" + ], + "event": "staff-shift-handoff", + "expected": { + "authorization_result": "eligible-only-after-every-required-gate", + "disclosure": { + "mode": "communication-state-only", + "surfaces": [ + "communication", + "care-team" + ], + "patient_message_key": "journey.handoff.review_in_progress", + "values_must_be_separately_released": true, + "atomic_response_required": true, + "no_store_required": true + }, + "state_transitions": [ + "thread-and-message-identities-remain-stable", + "content-free-routing-ownership-changes-append-only", + "no-eligible-destination-produces-unresolved-not-guessed-owner" + ], + "communication": { + "compose_state": "available-only-if-fresh-accountable-routing-exists", + "accepted_state_meaning": "server-acceptance-only", + "routing_state": "assigned-rerouted-or-unresolved", + "notification_state": "not-available", + "volatile_draft_action": "preserve-only-if-same-authorized-context" + }, + "nondisclosure": [ + "no-source-system-identifier", + "no-patient-principal-grant-encounter-or-staff-identifier", + "no-unreleased-clinical-value", + "no-withheld-resource-existence-or-denial-reason", + "no-internal-routing-pool-or-staffing-detail", + "no-approval-actor-or-internal-correction-target" + ], + "prohibited_inferences": [ + "named-staff-member-owns-or-read-thread", + "handoff-guarantees-response-time" + ], + "accessibility": { + "profile": "same-information-and-actions-through-supported-presentation", + "essential_information_in_imagery_permitted": false, + "color_only_state_permitted": false, + "patient_action_loss_permitted": false + }, + "audit": [ + "journey-evaluation-with-policy-and-case-id", + "authorization-and-current-context-outcome", + "disclosed-field-keys-without-values", + "communication-state-transition-without-message-body", + "withhold-correction-retraction-or-outage-reason-code-without-clinical-content" + ], + "offline": { + "phi_cache_permitted": false, + "stale_value_may_be_presented_as_current": false, + "queued_mutation_permitted": false, + "last_known_value_requires_separate_approved_policy": true + }, + "rollback": { + "kill_switch_required": true, + "release_withdrawal_required": true, + "cached_handle_and_volatile_draft_purge_required": true + } + }, + "required_approval_gates": [ + "named-product-and-contract-owner", + "approved-self-identity-session-and-recovery-design", + "approved-current-inpatient-source-and-lifecycle-design", + "approved-operation-specific-authorization-and-nondisclosure-matrix", + "approved-field-source-release-freshness-uncertainty-correction-language-and-offline-matrix", + "approved-clinical-content-and-patient-language", + "approved-communication-routing-replay-urgency-and-patient-visible-state-model", + "approved-representative-consent-scope-expiry-revocation-and-sensitive-service-policy", + "approved-accessibility-language-interpreter-and-accommodation-design", + "approved-privacy-security-threat-and-clinical-hazard-controls", + "approved-audit-monitoring-support-incident-rollback-and-kill-switch-plan", + "canonical-backend-ios-android-fixture-parity", + "default-off-nonproduction-integration-approval", + "approved-authoritative-workforce-and-shift-handoff-input" + ] + }, + { + "case_id": "discharge_estimate_corrected", + "family_id": "discharge-estimate", + "fixture_class": "synthetic-no-phi", + "execution_status": "held-not-runnable", + "actor": "self", + "encounter_state": "current-inpatient", + "preconditions": [ + "prior-discharge-estimate-was-released", + "new-estimate-is-separately-reviewed-and-released", + "correction-notice-copy-approved" + ], + "event": "estimated-discharge-date-changes", + "expected": { + "authorization_result": "eligible-only-after-every-required-gate", + "disclosure": { + "mode": "correction-replacement", + "surfaces": [ + "today", + "my-path" + ], + "patient_message_key": "journey.discharge.estimate_updated", + "values_must_be_separately_released": true, + "atomic_response_required": true, + "no_store_required": true + }, + "state_transitions": [ + "old-estimate-withdrawn-atomically", + "new-estimate-shown-with-can-change-and-confidence-context", + "internal-correction-target-and-reason-remain-hidden" + ], + "communication": { + "compose_state": "not-applicable", + "accepted_state_meaning": "not-applicable", + "routing_state": "not-applicable", + "notification_state": "not-available", + "volatile_draft_action": "clear-on-inactive-or-context-loss" + }, + "nondisclosure": [ + "no-source-system-identifier", + "no-patient-principal-grant-encounter-or-staff-identifier", + "no-unreleased-clinical-value", + "no-withheld-resource-existence-or-denial-reason", + "no-internal-routing-pool-or-staffing-detail", + "no-approval-actor-or-internal-correction-target" + ], + "prohibited_inferences": [ + "estimate-is-a-promise", + "changed-estimate-identifies-cause-or-accountability" + ], + "accessibility": { + "profile": "same-information-and-actions-through-supported-presentation", + "essential_information_in_imagery_permitted": false, + "color_only_state_permitted": false, + "patient_action_loss_permitted": false + }, + "audit": [ + "journey-evaluation-with-policy-and-case-id", + "authorization-and-current-context-outcome", + "disclosed-field-keys-without-values", + "communication-state-transition-without-message-body", + "withhold-correction-retraction-or-outage-reason-code-without-clinical-content" + ], + "offline": { + "phi_cache_permitted": false, + "stale_value_may_be_presented_as_current": false, + "queued_mutation_permitted": false, + "last_known_value_requires_separate_approved_policy": true + }, + "rollback": { + "kill_switch_required": true, + "release_withdrawal_required": true, + "cached_handle_and_volatile_draft_purge_required": true + } + }, + "required_approval_gates": [ + "named-product-and-contract-owner", + "approved-self-identity-session-and-recovery-design", + "approved-current-inpatient-source-and-lifecycle-design", + "approved-operation-specific-authorization-and-nondisclosure-matrix", + "approved-field-source-release-freshness-uncertainty-correction-language-and-offline-matrix", + "approved-clinical-content-and-patient-language", + "approved-communication-routing-replay-urgency-and-patient-visible-state-model", + "approved-representative-consent-scope-expiry-revocation-and-sensitive-service-policy", + "approved-accessibility-language-interpreter-and-accommodation-design", + "approved-privacy-security-threat-and-clinical-hazard-controls", + "approved-audit-monitoring-support-incident-rollback-and-kill-switch-plan", + "canonical-backend-ios-android-fixture-parity", + "default-off-nonproduction-integration-approval", + "approved-discharge-estimate-source-confidence-and-language-policy" + ] + }, + { + "case_id": "discharge_closes_open_thread", + "family_id": "discharge-with-open-thread", + "fixture_class": "synthetic-no-phi", + "execution_status": "held-not-runnable", + "actor": "self", + "encounter_state": "closed", + "preconditions": [ + "open-thread-existed-before-discharge", + "authoritative-discharge-reconciled", + "post-discharge-portal-retention-and-support-policy-not-approved" + ], + "event": "patient-discharged-while-thread-open", + "expected": { + "authorization_result": "indistinguishable-withhold", + "disclosure": { + "mode": "generic-withhold", + "surfaces": [ + "today", + "my-path", + "care-team", + "communication" + ], + "patient_message_key": "journey.discharge.access_changed", + "values_must_be_separately_released": true, + "atomic_response_required": true, + "no_store_required": true + }, + "state_transitions": [ + "current-inpatient-context-withdrawn", + "compose-capability-revoked-before-next-mutation", + "open-thread-closed-or-omitted-by-approved-lifecycle-policy", + "volatile-draft-and-cached-handle-purged" + ], + "communication": { + "compose_state": "unavailable", + "accepted_state_meaning": "historical-server-acceptance-not-current-access", + "routing_state": "closed-by-encounter-lifecycle", + "notification_state": "not-available", + "volatile_draft_action": "purge" + }, + "nondisclosure": [ + "no-source-system-identifier", + "no-patient-principal-grant-encounter-or-staff-identifier", + "no-unreleased-clinical-value", + "no-withheld-resource-existence-or-denial-reason", + "no-internal-routing-pool-or-staffing-detail", + "no-approval-actor-or-internal-correction-target" + ], + "prohibited_inferences": [ + "thread-never-existed", + "care-team-read-or-resolved-question", + "post-discharge-support-channel" + ], + "accessibility": { + "profile": "same-information-and-actions-through-supported-presentation", + "essential_information_in_imagery_permitted": false, + "color_only_state_permitted": false, + "patient_action_loss_permitted": false + }, + "audit": [ + "journey-evaluation-with-policy-and-case-id", + "authorization-and-current-context-outcome", + "disclosed-field-keys-without-values", + "communication-state-transition-without-message-body", + "withhold-correction-retraction-or-outage-reason-code-without-clinical-content" + ], + "offline": { + "phi_cache_permitted": false, + "stale_value_may_be_presented_as_current": false, + "queued_mutation_permitted": false, + "last_known_value_requires_separate_approved_policy": true + }, + "rollback": { + "kill_switch_required": true, + "release_withdrawal_required": true, + "cached_handle_and_volatile_draft_purge_required": true + } + }, + "required_approval_gates": [ + "named-product-and-contract-owner", + "approved-self-identity-session-and-recovery-design", + "approved-current-inpatient-source-and-lifecycle-design", + "approved-operation-specific-authorization-and-nondisclosure-matrix", + "approved-field-source-release-freshness-uncertainty-correction-language-and-offline-matrix", + "approved-clinical-content-and-patient-language", + "approved-communication-routing-replay-urgency-and-patient-visible-state-model", + "approved-representative-consent-scope-expiry-revocation-and-sensitive-service-policy", + "approved-accessibility-language-interpreter-and-accommodation-design", + "approved-privacy-security-threat-and-clinical-hazard-controls", + "approved-audit-monitoring-support-incident-rollback-and-kill-switch-plan", + "canonical-backend-ios-android-fixture-parity", + "default-off-nonproduction-integration-approval", + "approved-discharge-thread-retention-closure-and-portal-handoff-policy" + ] + }, + { + "case_id": "identity_correction_revokes_old_context", + "family_id": "identity-correction", + "fixture_class": "synthetic-no-phi", + "execution_status": "held-not-runnable", + "actor": "self", + "encounter_state": "requires-reproof", + "preconditions": [ + "identity-link-is-merged-corrected-or-superseded", + "old-session-and-context-handle-may-reference-prior-link" + ], + "event": "patient-identity-merge-or-correction", + "expected": { + "authorization_result": "indistinguishable-withhold", + "disclosure": { + "mode": "generic-withhold", + "surfaces": [ + "today", + "my-path", + "care-team", + "communication" + ], + "patient_message_key": "journey.identity.access_requires_review", + "values_must_be_separately_released": true, + "atomic_response_required": true, + "no_store_required": true + }, + "state_transitions": [ + "old-session-family-and-context-handles-revoked", + "cached-patient-state-and-drafts-purged", + "no-automatic-cross-link-or-record-union", + "new-access-requires-approved-reproof-and-authoritative-linkage" + ], + "communication": { + "compose_state": "not-applicable", + "accepted_state_meaning": "not-applicable", + "routing_state": "not-applicable", + "notification_state": "not-available", + "volatile_draft_action": "clear-on-inactive-or-context-loss" + }, + "nondisclosure": [ + "no-source-system-identifier", + "no-patient-principal-grant-encounter-or-staff-identifier", + "no-unreleased-clinical-value", + "no-withheld-resource-existence-or-denial-reason", + "no-internal-routing-pool-or-staffing-detail", + "no-approval-actor-or-internal-correction-target" + ], + "prohibited_inferences": [ + "other-record-or-identity-exists", + "merge-target-identifier", + "prior-and-corrected-record-content" + ], + "accessibility": { + "profile": "same-information-and-actions-through-supported-presentation", + "essential_information_in_imagery_permitted": false, + "color_only_state_permitted": false, + "patient_action_loss_permitted": false + }, + "audit": [ + "journey-evaluation-with-policy-and-case-id", + "authorization-and-current-context-outcome", + "disclosed-field-keys-without-values", + "communication-state-transition-without-message-body", + "withhold-correction-retraction-or-outage-reason-code-without-clinical-content" + ], + "offline": { + "phi_cache_permitted": false, + "stale_value_may_be_presented_as_current": false, + "queued_mutation_permitted": false, + "last_known_value_requires_separate_approved_policy": true + }, + "rollback": { + "kill_switch_required": true, + "release_withdrawal_required": true, + "cached_handle_and_volatile_draft_purge_required": true + } + }, + "required_approval_gates": [ + "named-product-and-contract-owner", + "approved-self-identity-session-and-recovery-design", + "approved-current-inpatient-source-and-lifecycle-design", + "approved-operation-specific-authorization-and-nondisclosure-matrix", + "approved-field-source-release-freshness-uncertainty-correction-language-and-offline-matrix", + "approved-clinical-content-and-patient-language", + "approved-communication-routing-replay-urgency-and-patient-visible-state-model", + "approved-representative-consent-scope-expiry-revocation-and-sensitive-service-policy", + "approved-accessibility-language-interpreter-and-accommodation-design", + "approved-privacy-security-threat-and-clinical-hazard-controls", + "approved-audit-monitoring-support-incident-rollback-and-kill-switch-plan", + "canonical-backend-ios-android-fixture-parity", + "default-off-nonproduction-integration-approval", + "approved-identity-merge-correction-reproof-and-audit-policy" + ] + }, + { + "case_id": "representative_invited_without_access", + "family_id": "representative-lifecycle", + "fixture_class": "synthetic-no-phi", + "execution_status": "held-not-runnable", + "actor": "representative-candidate", + "encounter_state": "current-inpatient", + "preconditions": [ + "invitation-created-in-future-approved-workflow", + "identity-proof-consent-scope-and-acceptance-incomplete" + ], + "event": "representative-invited", + "expected": { + "authorization_result": "indistinguishable-withhold", + "disclosure": { + "mode": "generic-withhold", + "surfaces": [], + "patient_message_key": "journey.representative.access_not_available", + "values_must_be_separately_released": true, + "atomic_response_required": true, + "no_store_required": true + }, + "state_transitions": [ + "invitation-never-grants-access", + "no-patient-context-handle-issued" + ], + "communication": { + "compose_state": "not-applicable", + "accepted_state_meaning": "not-applicable", + "routing_state": "not-applicable", + "notification_state": "not-available", + "volatile_draft_action": "clear-on-inactive-or-context-loss" + }, + "nondisclosure": [ + "no-source-system-identifier", + "no-patient-principal-grant-encounter-or-staff-identifier", + "no-unreleased-clinical-value", + "no-withheld-resource-existence-or-denial-reason", + "no-internal-routing-pool-or-staffing-detail", + "no-approval-actor-or-internal-correction-target" + ], + "prohibited_inferences": [ + "patient-has-an-admission", + "invitation-target-is-associated-with-patient" + ], + "accessibility": { + "profile": "same-information-and-actions-through-supported-presentation", + "essential_information_in_imagery_permitted": false, + "color_only_state_permitted": false, + "patient_action_loss_permitted": false + }, + "audit": [ + "journey-evaluation-with-policy-and-case-id", + "authorization-and-current-context-outcome", + "disclosed-field-keys-without-values", + "communication-state-transition-without-message-body", + "withhold-correction-retraction-or-outage-reason-code-without-clinical-content" + ], + "offline": { + "phi_cache_permitted": false, + "stale_value_may_be_presented_as_current": false, + "queued_mutation_permitted": false, + "last_known_value_requires_separate_approved_policy": true + }, + "rollback": { + "kill_switch_required": true, + "release_withdrawal_required": true, + "cached_handle_and_volatile_draft_purge_required": true + } + }, + "required_approval_gates": [ + "named-product-and-contract-owner", + "approved-self-identity-session-and-recovery-design", + "approved-current-inpatient-source-and-lifecycle-design", + "approved-operation-specific-authorization-and-nondisclosure-matrix", + "approved-field-source-release-freshness-uncertainty-correction-language-and-offline-matrix", + "approved-clinical-content-and-patient-language", + "approved-communication-routing-replay-urgency-and-patient-visible-state-model", + "approved-representative-consent-scope-expiry-revocation-and-sensitive-service-policy", + "approved-accessibility-language-interpreter-and-accommodation-design", + "approved-privacy-security-threat-and-clinical-hazard-controls", + "approved-audit-monitoring-support-incident-rollback-and-kill-switch-plan", + "canonical-backend-ios-android-fixture-parity", + "default-off-nonproduction-integration-approval", + "approved-representative-invitation-proofing-and-consent-policy" + ] + }, + { + "case_id": "representative_scoped_candidate", + "family_id": "representative-lifecycle", + "fixture_class": "synthetic-no-phi", + "execution_status": "held-not-runnable", + "actor": "representative-candidate", + "encounter_state": "current-inpatient", + "preconditions": [ + "representative-identity-and-relationship-verified", + "patient-consent-or-other-authority-current", + "operation-and-field-scopes-effective", + "sensitive-service-exclusions-evaluated" + ], + "event": "representative-scope-becomes-effective", + "expected": { + "authorization_result": "indistinguishable-withhold", + "disclosure": { + "mode": "generic-withhold", + "surfaces": [], + "patient_message_key": "journey.representative.held_pending_approval", + "values_must_be_separately_released": true, + "atomic_response_required": true, + "no_store_required": true + }, + "state_transitions": [ + "candidate-remains-held-until-representative-model-approved", + "future-access-must-be-operation-and-field-scoped", + "self-and-representative-realms-must-not-share-handles-or-caches" + ], + "communication": { + "compose_state": "not-applicable", + "accepted_state_meaning": "not-applicable", + "routing_state": "not-applicable", + "notification_state": "not-available", + "volatile_draft_action": "clear-on-inactive-or-context-loss" + }, + "nondisclosure": [ + "no-source-system-identifier", + "no-patient-principal-grant-encounter-or-staff-identifier", + "no-unreleased-clinical-value", + "no-withheld-resource-existence-or-denial-reason", + "no-internal-routing-pool-or-staffing-detail", + "no-approval-actor-or-internal-correction-target" + ], + "prohibited_inferences": [ + "all-patient-content-is-in-scope", + "representative-can-communicate-or-act-as-patient" + ], + "accessibility": { + "profile": "same-information-and-actions-through-supported-presentation", + "essential_information_in_imagery_permitted": false, + "color_only_state_permitted": false, + "patient_action_loss_permitted": false + }, + "audit": [ + "journey-evaluation-with-policy-and-case-id", + "authorization-and-current-context-outcome", + "disclosed-field-keys-without-values", + "communication-state-transition-without-message-body", + "withhold-correction-retraction-or-outage-reason-code-without-clinical-content" + ], + "offline": { + "phi_cache_permitted": false, + "stale_value_may_be_presented_as_current": false, + "queued_mutation_permitted": false, + "last_known_value_requires_separate_approved_policy": true + }, + "rollback": { + "kill_switch_required": true, + "release_withdrawal_required": true, + "cached_handle_and_volatile_draft_purge_required": true + } + }, + "required_approval_gates": [ + "named-product-and-contract-owner", + "approved-self-identity-session-and-recovery-design", + "approved-current-inpatient-source-and-lifecycle-design", + "approved-operation-specific-authorization-and-nondisclosure-matrix", + "approved-field-source-release-freshness-uncertainty-correction-language-and-offline-matrix", + "approved-clinical-content-and-patient-language", + "approved-communication-routing-replay-urgency-and-patient-visible-state-model", + "approved-representative-consent-scope-expiry-revocation-and-sensitive-service-policy", + "approved-accessibility-language-interpreter-and-accommodation-design", + "approved-privacy-security-threat-and-clinical-hazard-controls", + "approved-audit-monitoring-support-incident-rollback-and-kill-switch-plan", + "canonical-backend-ios-android-fixture-parity", + "default-off-nonproduction-integration-approval", + "approved-representative-operation-field-and-communication-scopes" + ] + }, + { + "case_id": "representative_scope_expired", + "family_id": "representative-lifecycle", + "fixture_class": "synthetic-no-phi", + "execution_status": "held-not-runnable", + "actor": "representative-candidate", + "encounter_state": "access-expired", + "preconditions": [ + "prior-representative-scope-reached-expiry", + "request-or-refresh-occurs-after-expiry" + ], + "event": "representative-scope-expires", + "expected": { + "authorization_result": "indistinguishable-withhold", + "disclosure": { + "mode": "generic-withhold", + "surfaces": [], + "patient_message_key": "journey.representative.access_not_available", + "values_must_be_separately_released": true, + "atomic_response_required": true, + "no_store_required": true + }, + "state_transitions": [ + "all-representative-capabilities-denied", + "context-handles-caches-and-drafts-purged", + "no-offline-display-after-expiry" + ], + "communication": { + "compose_state": "not-applicable", + "accepted_state_meaning": "not-applicable", + "routing_state": "not-applicable", + "notification_state": "not-available", + "volatile_draft_action": "clear-on-inactive-or-context-loss" + }, + "nondisclosure": [ + "no-source-system-identifier", + "no-patient-principal-grant-encounter-or-staff-identifier", + "no-unreleased-clinical-value", + "no-withheld-resource-existence-or-denial-reason", + "no-internal-routing-pool-or-staffing-detail", + "no-approval-actor-or-internal-correction-target" + ], + "prohibited_inferences": [ + "patient-or-encounter-current-state", + "whether-access-ended-by-time-or-another-reason" + ], + "accessibility": { + "profile": "same-information-and-actions-through-supported-presentation", + "essential_information_in_imagery_permitted": false, + "color_only_state_permitted": false, + "patient_action_loss_permitted": false + }, + "audit": [ + "journey-evaluation-with-policy-and-case-id", + "authorization-and-current-context-outcome", + "disclosed-field-keys-without-values", + "communication-state-transition-without-message-body", + "withhold-correction-retraction-or-outage-reason-code-without-clinical-content" + ], + "offline": { + "phi_cache_permitted": false, + "stale_value_may_be_presented_as_current": false, + "queued_mutation_permitted": false, + "last_known_value_requires_separate_approved_policy": true + }, + "rollback": { + "kill_switch_required": true, + "release_withdrawal_required": true, + "cached_handle_and_volatile_draft_purge_required": true + } + }, + "required_approval_gates": [ + "named-product-and-contract-owner", + "approved-self-identity-session-and-recovery-design", + "approved-current-inpatient-source-and-lifecycle-design", + "approved-operation-specific-authorization-and-nondisclosure-matrix", + "approved-field-source-release-freshness-uncertainty-correction-language-and-offline-matrix", + "approved-clinical-content-and-patient-language", + "approved-communication-routing-replay-urgency-and-patient-visible-state-model", + "approved-representative-consent-scope-expiry-revocation-and-sensitive-service-policy", + "approved-accessibility-language-interpreter-and-accommodation-design", + "approved-privacy-security-threat-and-clinical-hazard-controls", + "approved-audit-monitoring-support-incident-rollback-and-kill-switch-plan", + "canonical-backend-ios-android-fixture-parity", + "default-off-nonproduction-integration-approval", + "approved-representative-expiry-and-session-revocation-policy" + ] + }, + { + "case_id": "representative_scope_revoked", + "family_id": "representative-lifecycle", + "fixture_class": "synthetic-no-phi", + "execution_status": "held-not-runnable", + "actor": "representative-candidate", + "encounter_state": "access-revoked", + "preconditions": [ + "prior-representative-scope-revoked", + "request-mutation-refresh-or-offline-open-occurs-after-revocation" + ], + "event": "representative-scope-revoked", + "expected": { + "authorization_result": "indistinguishable-withhold", + "disclosure": { + "mode": "generic-withhold", + "surfaces": [], + "patient_message_key": "journey.representative.access_not_available", + "values_must_be_separately_released": true, + "atomic_response_required": true, + "no_store_required": true + }, + "state_transitions": [ + "authorization-rechecked-before-every-disclosure-and-mutation", + "session-family-context-handles-caches-and-drafts-purged", + "queued-or-replayed-mutation-rejected" + ], + "communication": { + "compose_state": "not-applicable", + "accepted_state_meaning": "not-applicable", + "routing_state": "not-applicable", + "notification_state": "not-available", + "volatile_draft_action": "clear-on-inactive-or-context-loss" + }, + "nondisclosure": [ + "no-source-system-identifier", + "no-patient-principal-grant-encounter-or-staff-identifier", + "no-unreleased-clinical-value", + "no-withheld-resource-existence-or-denial-reason", + "no-internal-routing-pool-or-staffing-detail", + "no-approval-actor-or-internal-correction-target" + ], + "prohibited_inferences": [ + "who-revoked-access", + "revocation-reason", + "patient-or-encounter-current-state" + ], + "accessibility": { + "profile": "same-information-and-actions-through-supported-presentation", + "essential_information_in_imagery_permitted": false, + "color_only_state_permitted": false, + "patient_action_loss_permitted": false + }, + "audit": [ + "journey-evaluation-with-policy-and-case-id", + "authorization-and-current-context-outcome", + "disclosed-field-keys-without-values", + "communication-state-transition-without-message-body", + "withhold-correction-retraction-or-outage-reason-code-without-clinical-content" + ], + "offline": { + "phi_cache_permitted": false, + "stale_value_may_be_presented_as_current": false, + "queued_mutation_permitted": false, + "last_known_value_requires_separate_approved_policy": true + }, + "rollback": { + "kill_switch_required": true, + "release_withdrawal_required": true, + "cached_handle_and_volatile_draft_purge_required": true + } + }, + "required_approval_gates": [ + "named-product-and-contract-owner", + "approved-self-identity-session-and-recovery-design", + "approved-current-inpatient-source-and-lifecycle-design", + "approved-operation-specific-authorization-and-nondisclosure-matrix", + "approved-field-source-release-freshness-uncertainty-correction-language-and-offline-matrix", + "approved-clinical-content-and-patient-language", + "approved-communication-routing-replay-urgency-and-patient-visible-state-model", + "approved-representative-consent-scope-expiry-revocation-and-sensitive-service-policy", + "approved-accessibility-language-interpreter-and-accommodation-design", + "approved-privacy-security-threat-and-clinical-hazard-controls", + "approved-audit-monitoring-support-incident-rollback-and-kill-switch-plan", + "canonical-backend-ios-android-fixture-parity", + "default-off-nonproduction-integration-approval", + "approved-representative-revocation-propagation-and-support-policy" + ] + }, + { + "case_id": "limited_english_interpreter_support", + "family_id": "language-and-interpreter", + "fixture_class": "synthetic-no-phi", + "execution_status": "held-not-runnable", + "actor": "self", + "encounter_state": "current-inpatient", + "preconditions": [ + "account-language-preference-approved", + "each-patient-visible-field-has-approved-language-release", + "interpreter-request-status-has-separate-authoritative-source" + ], + "event": "limited-english-patient-uses-approved-language-and-interpreter-support", + "expected": { + "authorization_result": "eligible-only-after-every-required-gate", + "disclosure": { + "mode": "released-fields-only", + "surfaces": [ + "today", + "my-path", + "care-team", + "communication" + ], + "patient_message_key": "journey.language.approved_content_and_interpreter_status", + "values_must_be_separately_released": true, + "atomic_response_required": true, + "no_store_required": true + }, + "state_transitions": [ + "approved-translation-or-approved-source-language-selected-per-field", + "unapproved-translation-withheld-without-machine-translation-fallback", + "interpreter-status-shown-only-if-separately-released" + ], + "communication": { + "compose_state": "not-applicable", + "accepted_state_meaning": "not-applicable", + "routing_state": "not-applicable", + "notification_state": "not-available", + "volatile_draft_action": "clear-on-inactive-or-context-loss" + }, + "nondisclosure": [ + "no-source-system-identifier", + "no-patient-principal-grant-encounter-or-staff-identifier", + "no-unreleased-clinical-value", + "no-withheld-resource-existence-or-denial-reason", + "no-internal-routing-pool-or-staffing-detail", + "no-approval-actor-or-internal-correction-target" + ], + "prohibited_inferences": [ + "interpreter-is-confirmed-from-language-preference-alone", + "unreviewed-machine-translation-is-clinical-content" + ], + "accessibility": { + "profile": "same-information-and-actions-through-supported-presentation", + "essential_information_in_imagery_permitted": false, + "color_only_state_permitted": false, + "patient_action_loss_permitted": false + }, + "audit": [ + "journey-evaluation-with-policy-and-case-id", + "authorization-and-current-context-outcome", + "disclosed-field-keys-without-values", + "communication-state-transition-without-message-body", + "withhold-correction-retraction-or-outage-reason-code-without-clinical-content" + ], + "offline": { + "phi_cache_permitted": false, + "stale_value_may_be_presented_as_current": false, + "queued_mutation_permitted": false, + "last_known_value_requires_separate_approved_policy": true + }, + "rollback": { + "kill_switch_required": true, + "release_withdrawal_required": true, + "cached_handle_and_volatile_draft_purge_required": true + } + }, + "required_approval_gates": [ + "named-product-and-contract-owner", + "approved-self-identity-session-and-recovery-design", + "approved-current-inpatient-source-and-lifecycle-design", + "approved-operation-specific-authorization-and-nondisclosure-matrix", + "approved-field-source-release-freshness-uncertainty-correction-language-and-offline-matrix", + "approved-clinical-content-and-patient-language", + "approved-communication-routing-replay-urgency-and-patient-visible-state-model", + "approved-representative-consent-scope-expiry-revocation-and-sensitive-service-policy", + "approved-accessibility-language-interpreter-and-accommodation-design", + "approved-privacy-security-threat-and-clinical-hazard-controls", + "approved-audit-monitoring-support-incident-rollback-and-kill-switch-plan", + "canonical-backend-ios-android-fixture-parity", + "default-off-nonproduction-integration-approval", + "approved-professional-translation-fallback-and-interpreter-operating-model" + ] + }, + { + "case_id": "visual_accommodation_equivalence", + "family_id": "accommodations", + "fixture_class": "synthetic-no-phi", + "execution_status": "held-not-runnable", + "actor": "self", + "encounter_state": "current-inpatient", + "preconditions": [ + "visual-accommodation-preference-or-supported-system-setting-active" + ], + "event": "patient-uses-visual-accommodation", + "expected": { + "authorization_result": "eligible-only-after-every-required-gate", + "disclosure": { + "mode": "accessibility-equivalent", + "surfaces": [ + "today", + "my-path", + "care-team", + "communication" + ], + "patient_message_key": "journey.accommodation.visual", + "values_must_be_separately_released": true, + "atomic_response_required": true, + "no_store_required": true + }, + "state_transitions": [ + "text-reflows-at-supported-system-size", + "decorative-imagery-can-be-hidden", + "state-retains-text-shape-and-noncolor-cue" + ], + "communication": { + "compose_state": "not-applicable", + "accepted_state_meaning": "not-applicable", + "routing_state": "not-applicable", + "notification_state": "not-available", + "volatile_draft_action": "clear-on-inactive-or-context-loss" + }, + "nondisclosure": [ + "no-source-system-identifier", + "no-patient-principal-grant-encounter-or-staff-identifier", + "no-unreleased-clinical-value", + "no-withheld-resource-existence-or-denial-reason", + "no-internal-routing-pool-or-staffing-detail", + "no-approval-actor-or-internal-correction-target" + ], + "prohibited_inferences": [ + "hidden-image-removes-information", + "color-alone-communicates-status" + ], + "accessibility": { + "profile": "same-information-and-actions-through-supported-presentation", + "essential_information_in_imagery_permitted": false, + "color_only_state_permitted": false, + "patient_action_loss_permitted": false + }, + "audit": [ + "journey-evaluation-with-policy-and-case-id", + "authorization-and-current-context-outcome", + "disclosed-field-keys-without-values", + "communication-state-transition-without-message-body", + "withhold-correction-retraction-or-outage-reason-code-without-clinical-content" + ], + "offline": { + "phi_cache_permitted": false, + "stale_value_may_be_presented_as_current": false, + "queued_mutation_permitted": false, + "last_known_value_requires_separate_approved_policy": true + }, + "rollback": { + "kill_switch_required": true, + "release_withdrawal_required": true, + "cached_handle_and_volatile_draft_purge_required": true + } + }, + "required_approval_gates": [ + "named-product-and-contract-owner", + "approved-self-identity-session-and-recovery-design", + "approved-current-inpatient-source-and-lifecycle-design", + "approved-operation-specific-authorization-and-nondisclosure-matrix", + "approved-field-source-release-freshness-uncertainty-correction-language-and-offline-matrix", + "approved-clinical-content-and-patient-language", + "approved-communication-routing-replay-urgency-and-patient-visible-state-model", + "approved-representative-consent-scope-expiry-revocation-and-sensitive-service-policy", + "approved-accessibility-language-interpreter-and-accommodation-design", + "approved-privacy-security-threat-and-clinical-hazard-controls", + "approved-audit-monitoring-support-incident-rollback-and-kill-switch-plan", + "canonical-backend-ios-android-fixture-parity", + "default-off-nonproduction-integration-approval", + "approved-screen-reader-magnification-contrast-and-visual-design-review" + ] + }, + { + "case_id": "hearing_accommodation_equivalence", + "family_id": "accommodations", + "fixture_class": "synthetic-no-phi", + "execution_status": "held-not-runnable", + "actor": "self", + "encounter_state": "current-inpatient", + "preconditions": [ + "hearing-accommodation-required", + "future-audio-or-video-content-may-be-present" + ], + "event": "patient-uses-hearing-accommodation", + "expected": { + "authorization_result": "eligible-only-after-every-required-gate", + "disclosure": { + "mode": "accessibility-equivalent", + "surfaces": [ + "today", + "my-path", + "care-team", + "communication" + ], + "patient_message_key": "journey.accommodation.hearing", + "values_must_be_separately_released": true, + "atomic_response_required": true, + "no_store_required": true + }, + "state_transitions": [ + "no-essential-alert-relies-on-sound-alone", + "approved-caption-or-transcript-required-for-future-media", + "communication-state-remains-visible-in-text" + ], + "communication": { + "compose_state": "not-applicable", + "accepted_state_meaning": "not-applicable", + "routing_state": "not-applicable", + "notification_state": "not-available", + "volatile_draft_action": "clear-on-inactive-or-context-loss" + }, + "nondisclosure": [ + "no-source-system-identifier", + "no-patient-principal-grant-encounter-or-staff-identifier", + "no-unreleased-clinical-value", + "no-withheld-resource-existence-or-denial-reason", + "no-internal-routing-pool-or-staffing-detail", + "no-approval-actor-or-internal-correction-target" + ], + "prohibited_inferences": [ + "audio-cue-alone-conveys-urgency-or-completion" + ], + "accessibility": { + "profile": "same-information-and-actions-through-supported-presentation", + "essential_information_in_imagery_permitted": false, + "color_only_state_permitted": false, + "patient_action_loss_permitted": false + }, + "audit": [ + "journey-evaluation-with-policy-and-case-id", + "authorization-and-current-context-outcome", + "disclosed-field-keys-without-values", + "communication-state-transition-without-message-body", + "withhold-correction-retraction-or-outage-reason-code-without-clinical-content" + ], + "offline": { + "phi_cache_permitted": false, + "stale_value_may_be_presented_as_current": false, + "queued_mutation_permitted": false, + "last_known_value_requires_separate_approved_policy": true + }, + "rollback": { + "kill_switch_required": true, + "release_withdrawal_required": true, + "cached_handle_and_volatile_draft_purge_required": true + } + }, + "required_approval_gates": [ + "named-product-and-contract-owner", + "approved-self-identity-session-and-recovery-design", + "approved-current-inpatient-source-and-lifecycle-design", + "approved-operation-specific-authorization-and-nondisclosure-matrix", + "approved-field-source-release-freshness-uncertainty-correction-language-and-offline-matrix", + "approved-clinical-content-and-patient-language", + "approved-communication-routing-replay-urgency-and-patient-visible-state-model", + "approved-representative-consent-scope-expiry-revocation-and-sensitive-service-policy", + "approved-accessibility-language-interpreter-and-accommodation-design", + "approved-privacy-security-threat-and-clinical-hazard-controls", + "approved-audit-monitoring-support-incident-rollback-and-kill-switch-plan", + "canonical-backend-ios-android-fixture-parity", + "default-off-nonproduction-integration-approval", + "approved-caption-transcript-and-hearing-access-review" + ] + }, + { + "case_id": "motor_accommodation_equivalence", + "family_id": "accommodations", + "fixture_class": "synthetic-no-phi", + "execution_status": "held-not-runnable", + "actor": "self", + "encounter_state": "current-inpatient", + "preconditions": [ + "motor-or-alternative-input-accommodation-required" + ], + "event": "patient-uses-motor-or-alternative-input", + "expected": { + "authorization_result": "eligible-only-after-every-required-gate", + "disclosure": { + "mode": "accessibility-equivalent", + "surfaces": [ + "today", + "my-path", + "care-team", + "communication" + ], + "patient_message_key": "journey.accommodation.motor", + "values_must_be_separately_released": true, + "atomic_response_required": true, + "no_store_required": true + }, + "state_transitions": [ + "all-actions-reachable-with-supported-alternative-input", + "target-size-and-focus-order-retained", + "timeout-does-not-discard-unsubmitted-input-without-warning" + ], + "communication": { + "compose_state": "not-applicable", + "accepted_state_meaning": "not-applicable", + "routing_state": "not-applicable", + "notification_state": "not-available", + "volatile_draft_action": "clear-on-inactive-or-context-loss" + }, + "nondisclosure": [ + "no-source-system-identifier", + "no-patient-principal-grant-encounter-or-staff-identifier", + "no-unreleased-clinical-value", + "no-withheld-resource-existence-or-denial-reason", + "no-internal-routing-pool-or-staffing-detail", + "no-approval-actor-or-internal-correction-target" + ], + "prohibited_inferences": [ + "gesture-only-action-is-equivalent" + ], + "accessibility": { + "profile": "same-information-and-actions-through-supported-presentation", + "essential_information_in_imagery_permitted": false, + "color_only_state_permitted": false, + "patient_action_loss_permitted": false + }, + "audit": [ + "journey-evaluation-with-policy-and-case-id", + "authorization-and-current-context-outcome", + "disclosed-field-keys-without-values", + "communication-state-transition-without-message-body", + "withhold-correction-retraction-or-outage-reason-code-without-clinical-content" + ], + "offline": { + "phi_cache_permitted": false, + "stale_value_may_be_presented_as_current": false, + "queued_mutation_permitted": false, + "last_known_value_requires_separate_approved_policy": true + }, + "rollback": { + "kill_switch_required": true, + "release_withdrawal_required": true, + "cached_handle_and_volatile_draft_purge_required": true + } + }, + "required_approval_gates": [ + "named-product-and-contract-owner", + "approved-self-identity-session-and-recovery-design", + "approved-current-inpatient-source-and-lifecycle-design", + "approved-operation-specific-authorization-and-nondisclosure-matrix", + "approved-field-source-release-freshness-uncertainty-correction-language-and-offline-matrix", + "approved-clinical-content-and-patient-language", + "approved-communication-routing-replay-urgency-and-patient-visible-state-model", + "approved-representative-consent-scope-expiry-revocation-and-sensitive-service-policy", + "approved-accessibility-language-interpreter-and-accommodation-design", + "approved-privacy-security-threat-and-clinical-hazard-controls", + "approved-audit-monitoring-support-incident-rollback-and-kill-switch-plan", + "canonical-backend-ios-android-fixture-parity", + "default-off-nonproduction-integration-approval", + "approved-switch-control-voice-control-keyboard-and-target-review" + ] + }, + { + "case_id": "cognitive_accommodation_equivalence", + "family_id": "accommodations", + "fixture_class": "synthetic-no-phi", + "execution_status": "held-not-runnable", + "actor": "self", + "encounter_state": "current-inpatient", + "preconditions": [ + "cognitive-accommodation-required" + ], + "event": "patient-uses-cognitive-accommodation", + "expected": { + "authorization_result": "eligible-only-after-every-required-gate", + "disclosure": { + "mode": "accessibility-equivalent", + "surfaces": [ + "today", + "my-path", + "care-team", + "communication" + ], + "patient_message_key": "journey.accommodation.cognitive", + "values_must_be_separately_released": true, + "atomic_response_required": true, + "no_store_required": true + }, + "state_transitions": [ + "one-primary-purpose-per-step", + "timing-uncertainty-and-changes-explained-consistently", + "urgent-help-path-remains-distinct-from-routine-message" + ], + "communication": { + "compose_state": "not-applicable", + "accepted_state_meaning": "not-applicable", + "routing_state": "not-applicable", + "notification_state": "not-available", + "volatile_draft_action": "clear-on-inactive-or-context-loss" + }, + "nondisclosure": [ + "no-source-system-identifier", + "no-patient-principal-grant-encounter-or-staff-identifier", + "no-unreleased-clinical-value", + "no-withheld-resource-existence-or-denial-reason", + "no-internal-routing-pool-or-staffing-detail", + "no-approval-actor-or-internal-correction-target" + ], + "prohibited_inferences": [ + "simplified-copy-removes-material-risk-or-uncertainty" + ], + "accessibility": { + "profile": "same-information-and-actions-through-supported-presentation", + "essential_information_in_imagery_permitted": false, + "color_only_state_permitted": false, + "patient_action_loss_permitted": false + }, + "audit": [ + "journey-evaluation-with-policy-and-case-id", + "authorization-and-current-context-outcome", + "disclosed-field-keys-without-values", + "communication-state-transition-without-message-body", + "withhold-correction-retraction-or-outage-reason-code-without-clinical-content" + ], + "offline": { + "phi_cache_permitted": false, + "stale_value_may_be_presented_as_current": false, + "queued_mutation_permitted": false, + "last_known_value_requires_separate_approved_policy": true + }, + "rollback": { + "kill_switch_required": true, + "release_withdrawal_required": true, + "cached_handle_and_volatile_draft_purge_required": true + } + }, + "required_approval_gates": [ + "named-product-and-contract-owner", + "approved-self-identity-session-and-recovery-design", + "approved-current-inpatient-source-and-lifecycle-design", + "approved-operation-specific-authorization-and-nondisclosure-matrix", + "approved-field-source-release-freshness-uncertainty-correction-language-and-offline-matrix", + "approved-clinical-content-and-patient-language", + "approved-communication-routing-replay-urgency-and-patient-visible-state-model", + "approved-representative-consent-scope-expiry-revocation-and-sensitive-service-policy", + "approved-accessibility-language-interpreter-and-accommodation-design", + "approved-privacy-security-threat-and-clinical-hazard-controls", + "approved-audit-monitoring-support-incident-rollback-and-kill-switch-plan", + "canonical-backend-ios-android-fixture-parity", + "default-off-nonproduction-integration-approval", + "approved-cognitive-load-comprehension-and-patient-advisor-review" + ] + }, + { + "case_id": "low_literacy_teach_back_equivalence", + "family_id": "accommodations", + "fixture_class": "synthetic-no-phi", + "execution_status": "held-not-runnable", + "actor": "self", + "encounter_state": "current-inpatient", + "preconditions": [ + "low-literacy-support-required", + "education-or-teach-back-capability-future-approved" + ], + "event": "patient-uses-low-literacy-and-teach-back-support", + "expected": { + "authorization_result": "eligible-only-after-every-required-gate", + "disclosure": { + "mode": "accessibility-equivalent", + "surfaces": [ + "my-path", + "care-team", + "communication" + ], + "patient_message_key": "journey.accommodation.low_literacy", + "values_must_be_separately_released": true, + "atomic_response_required": true, + "no_store_required": true + }, + "state_transitions": [ + "approved-plain-language-content-retains-clinical-meaning", + "teach-back-response-recorded-only-through-approved-capability", + "nonresponse-never-treated-as-understanding-or-refusal" + ], + "communication": { + "compose_state": "not-applicable", + "accepted_state_meaning": "not-applicable", + "routing_state": "not-applicable", + "notification_state": "not-available", + "volatile_draft_action": "clear-on-inactive-or-context-loss" + }, + "nondisclosure": [ + "no-source-system-identifier", + "no-patient-principal-grant-encounter-or-staff-identifier", + "no-unreleased-clinical-value", + "no-withheld-resource-existence-or-denial-reason", + "no-internal-routing-pool-or-staffing-detail", + "no-approval-actor-or-internal-correction-target" + ], + "prohibited_inferences": [ + "reading-level-predicts-capacity", + "completed-screen-implies-understanding" + ], + "accessibility": { + "profile": "same-information-and-actions-through-supported-presentation", + "essential_information_in_imagery_permitted": false, + "color_only_state_permitted": false, + "patient_action_loss_permitted": false + }, + "audit": [ + "journey-evaluation-with-policy-and-case-id", + "authorization-and-current-context-outcome", + "disclosed-field-keys-without-values", + "communication-state-transition-without-message-body", + "withhold-correction-retraction-or-outage-reason-code-without-clinical-content" + ], + "offline": { + "phi_cache_permitted": false, + "stale_value_may_be_presented_as_current": false, + "queued_mutation_permitted": false, + "last_known_value_requires_separate_approved_policy": true + }, + "rollback": { + "kill_switch_required": true, + "release_withdrawal_required": true, + "cached_handle_and_volatile_draft_purge_required": true + } + }, + "required_approval_gates": [ + "named-product-and-contract-owner", + "approved-self-identity-session-and-recovery-design", + "approved-current-inpatient-source-and-lifecycle-design", + "approved-operation-specific-authorization-and-nondisclosure-matrix", + "approved-field-source-release-freshness-uncertainty-correction-language-and-offline-matrix", + "approved-clinical-content-and-patient-language", + "approved-communication-routing-replay-urgency-and-patient-visible-state-model", + "approved-representative-consent-scope-expiry-revocation-and-sensitive-service-policy", + "approved-accessibility-language-interpreter-and-accommodation-design", + "approved-privacy-security-threat-and-clinical-hazard-controls", + "approved-audit-monitoring-support-incident-rollback-and-kill-switch-plan", + "canonical-backend-ios-android-fixture-parity", + "default-off-nonproduction-integration-approval", + "approved-health-literacy-education-and-teach-back-policy" + ] + }, + { + "case_id": "sensitive_data_fails_closed", + "family_id": "sensitive-data", + "fixture_class": "synthetic-no-phi", + "execution_status": "held-not-runnable", + "actor": "self", + "encounter_state": "current-inpatient", + "preconditions": [ + "resource-or-encounter-requires-sensitive-service-policy", + "effective-sensitive-service-authorization-is-absent-ambiguous-or-denied" + ], + "event": "sensitive-resource-requested", + "expected": { + "authorization_result": "indistinguishable-withhold", + "disclosure": { + "mode": "generic-withhold", + "surfaces": [], + "patient_message_key": "journey.sensitive.access_not_available", + "values_must_be_separately_released": true, + "atomic_response_required": true, + "no_store_required": true + }, + "state_transitions": [ + "no-field-section-thread-or-capability-disclosed", + "cached-value-handle-and-draft-purged", + "denial-indistinguishable-from-unknown-or-unavailable" + ], + "communication": { + "compose_state": "not-applicable", + "accepted_state_meaning": "not-applicable", + "routing_state": "not-applicable", + "notification_state": "not-available", + "volatile_draft_action": "clear-on-inactive-or-context-loss" + }, + "nondisclosure": [ + "no-source-system-identifier", + "no-patient-principal-grant-encounter-or-staff-identifier", + "no-unreleased-clinical-value", + "no-withheld-resource-existence-or-denial-reason", + "no-internal-routing-pool-or-staffing-detail", + "no-approval-actor-or-internal-correction-target" + ], + "prohibited_inferences": [ + "sensitive-service-exists", + "resource-type", + "restriction-policy-or-denial-reason" + ], + "accessibility": { + "profile": "same-information-and-actions-through-supported-presentation", + "essential_information_in_imagery_permitted": false, + "color_only_state_permitted": false, + "patient_action_loss_permitted": false + }, + "audit": [ + "journey-evaluation-with-policy-and-case-id", + "authorization-and-current-context-outcome", + "disclosed-field-keys-without-values", + "communication-state-transition-without-message-body", + "withhold-correction-retraction-or-outage-reason-code-without-clinical-content" + ], + "offline": { + "phi_cache_permitted": false, + "stale_value_may_be_presented_as_current": false, + "queued_mutation_permitted": false, + "last_known_value_requires_separate_approved_policy": true + }, + "rollback": { + "kill_switch_required": true, + "release_withdrawal_required": true, + "cached_handle_and_volatile_draft_purge_required": true + } + }, + "required_approval_gates": [ + "named-product-and-contract-owner", + "approved-self-identity-session-and-recovery-design", + "approved-current-inpatient-source-and-lifecycle-design", + "approved-operation-specific-authorization-and-nondisclosure-matrix", + "approved-field-source-release-freshness-uncertainty-correction-language-and-offline-matrix", + "approved-clinical-content-and-patient-language", + "approved-communication-routing-replay-urgency-and-patient-visible-state-model", + "approved-representative-consent-scope-expiry-revocation-and-sensitive-service-policy", + "approved-accessibility-language-interpreter-and-accommodation-design", + "approved-privacy-security-threat-and-clinical-hazard-controls", + "approved-audit-monitoring-support-incident-rollback-and-kill-switch-plan", + "canonical-backend-ios-android-fixture-parity", + "default-off-nonproduction-integration-approval", + "approved-sensitive-service-legal-privacy-clinical-and-representative-policy" + ] + }, + { + "case_id": "source_outage_sections_not_available", + "family_id": "source-outage-and-staleness", + "fixture_class": "synthetic-no-phi", + "execution_status": "held-not-runnable", + "actor": "self", + "encounter_state": "current-inpatient", + "preconditions": [ + "required-authoritative-source-is-unavailable", + "complete-fresh-evaluation-cannot-finish" + ], + "event": "authoritative-source-outage", + "expected": { + "authorization_result": "eligible-only-after-every-required-gate", + "disclosure": { + "mode": "not-available", + "surfaces": [ + "today", + "my-path", + "care-team" + ], + "patient_message_key": "journey.source.temporarily_not_available", + "values_must_be_separately_released": true, + "atomic_response_required": true, + "no_store_required": true + }, + "state_transitions": [ + "affected-sections-become-not-available-not-released-empty", + "unaffected-separately-governed-fields-may-remain", + "last-known-value-not-presented-as-current" + ], + "communication": { + "compose_state": "not-applicable", + "accepted_state_meaning": "not-applicable", + "routing_state": "not-applicable", + "notification_state": "not-available", + "volatile_draft_action": "clear-on-inactive-or-context-loss" + }, + "nondisclosure": [ + "no-source-system-identifier", + "no-patient-principal-grant-encounter-or-staff-identifier", + "no-unreleased-clinical-value", + "no-withheld-resource-existence-or-denial-reason", + "no-internal-routing-pool-or-staffing-detail", + "no-approval-actor-or-internal-correction-target" + ], + "prohibited_inferences": [ + "no-care-is-planned", + "source-outage-means-discharge-transfer-or-cancellation" + ], + "accessibility": { + "profile": "same-information-and-actions-through-supported-presentation", + "essential_information_in_imagery_permitted": false, + "color_only_state_permitted": false, + "patient_action_loss_permitted": false + }, + "audit": [ + "journey-evaluation-with-policy-and-case-id", + "authorization-and-current-context-outcome", + "disclosed-field-keys-without-values", + "communication-state-transition-without-message-body", + "withhold-correction-retraction-or-outage-reason-code-without-clinical-content" + ], + "offline": { + "phi_cache_permitted": false, + "stale_value_may_be_presented_as_current": false, + "queued_mutation_permitted": false, + "last_known_value_requires_separate_approved_policy": true + }, + "rollback": { + "kill_switch_required": true, + "release_withdrawal_required": true, + "cached_handle_and_volatile_draft_purge_required": true + } + }, + "required_approval_gates": [ + "named-product-and-contract-owner", + "approved-self-identity-session-and-recovery-design", + "approved-current-inpatient-source-and-lifecycle-design", + "approved-operation-specific-authorization-and-nondisclosure-matrix", + "approved-field-source-release-freshness-uncertainty-correction-language-and-offline-matrix", + "approved-clinical-content-and-patient-language", + "approved-communication-routing-replay-urgency-and-patient-visible-state-model", + "approved-representative-consent-scope-expiry-revocation-and-sensitive-service-policy", + "approved-accessibility-language-interpreter-and-accommodation-design", + "approved-privacy-security-threat-and-clinical-hazard-controls", + "approved-audit-monitoring-support-incident-rollback-and-kill-switch-plan", + "canonical-backend-ios-android-fixture-parity", + "default-off-nonproduction-integration-approval", + "approved-source-outage-partial-response-downtime-and-support-policy" + ] + }, + { + "case_id": "stale_projection_policy_evaluated_per_field", + "family_id": "source-outage-and-staleness", + "fixture_class": "synthetic-no-phi", + "execution_status": "held-not-runnable", + "actor": "self", + "encounter_state": "current-inpatient", + "preconditions": [ + "released-field-observation-is-older-than-current-source-policy-target", + "field-specific-stale-display-policy-evaluated" + ], + "event": "projection-becomes-stale", + "expected": { + "authorization_result": "eligible-only-after-every-required-gate", + "disclosure": { + "mode": "approved-stale-or-not-available", + "surfaces": [ + "today", + "my-path", + "care-team" + ], + "patient_message_key": "journey.source.stale_policy_applied", + "values_must_be_separately_released": true, + "atomic_response_required": true, + "no_store_required": true + }, + "state_transitions": [ + "approved-stale-field-shows-observed-time-and-patient-notice", + "field-without-approved-stale-policy-is-withheld", + "document-level-freshness-never-overrides-field-state" + ], + "communication": { + "compose_state": "not-applicable", + "accepted_state_meaning": "not-applicable", + "routing_state": "not-applicable", + "notification_state": "not-available", + "volatile_draft_action": "clear-on-inactive-or-context-loss" + }, + "nondisclosure": [ + "no-source-system-identifier", + "no-patient-principal-grant-encounter-or-staff-identifier", + "no-unreleased-clinical-value", + "no-withheld-resource-existence-or-denial-reason", + "no-internal-routing-pool-or-staffing-detail", + "no-approval-actor-or-internal-correction-target" + ], + "prohibited_inferences": [ + "stale-value-is-current", + "all-fields-share-the-same-freshness" + ], + "accessibility": { + "profile": "same-information-and-actions-through-supported-presentation", + "essential_information_in_imagery_permitted": false, + "color_only_state_permitted": false, + "patient_action_loss_permitted": false + }, + "audit": [ + "journey-evaluation-with-policy-and-case-id", + "authorization-and-current-context-outcome", + "disclosed-field-keys-without-values", + "communication-state-transition-without-message-body", + "withhold-correction-retraction-or-outage-reason-code-without-clinical-content" + ], + "offline": { + "phi_cache_permitted": false, + "stale_value_may_be_presented_as_current": false, + "queued_mutation_permitted": false, + "last_known_value_requires_separate_approved_policy": true + }, + "rollback": { + "kill_switch_required": true, + "release_withdrawal_required": true, + "cached_handle_and_volatile_draft_purge_required": true + } + }, + "required_approval_gates": [ + "named-product-and-contract-owner", + "approved-self-identity-session-and-recovery-design", + "approved-current-inpatient-source-and-lifecycle-design", + "approved-operation-specific-authorization-and-nondisclosure-matrix", + "approved-field-source-release-freshness-uncertainty-correction-language-and-offline-matrix", + "approved-clinical-content-and-patient-language", + "approved-communication-routing-replay-urgency-and-patient-visible-state-model", + "approved-representative-consent-scope-expiry-revocation-and-sensitive-service-policy", + "approved-accessibility-language-interpreter-and-accommodation-design", + "approved-privacy-security-threat-and-clinical-hazard-controls", + "approved-audit-monitoring-support-incident-rollback-and-kill-switch-plan", + "canonical-backend-ios-android-fixture-parity", + "default-off-nonproduction-integration-approval", + "approved-field-specific-staleness-threshold-copy-and-withhold-policy" + ] + }, + { + "case_id": "incorrect_content_retracted", + "family_id": "content-retraction-and-correction", + "fixture_class": "synthetic-no-phi", + "execution_status": "held-not-runnable", + "actor": "self", + "encounter_state": "current-inpatient", + "preconditions": [ + "patient-facing-value-was-released", + "effective-retraction-fact-withdraws-that-release", + "no-approved-replacement-is-effective" + ], + "event": "incorrect-patient-facing-content-retracted", + "expected": { + "authorization_result": "eligible-only-after-every-required-gate", + "disclosure": { + "mode": "not-available", + "surfaces": [ + "today", + "my-path", + "care-team", + "communication" + ], + "patient_message_key": "journey.content.withdrawn", + "values_must_be_separately_released": true, + "atomic_response_required": true, + "no_store_required": true + }, + "state_transitions": [ + "withdrawn-value-removed-atomically-from-all-surfaces", + "cached-and-offline-copies-purged", + "no-internal-reason-actor-or-target-identifier-disclosed" + ], + "communication": { + "compose_state": "not-applicable", + "accepted_state_meaning": "not-applicable", + "routing_state": "not-applicable", + "notification_state": "not-available", + "volatile_draft_action": "clear-on-inactive-or-context-loss" + }, + "nondisclosure": [ + "no-source-system-identifier", + "no-patient-principal-grant-encounter-or-staff-identifier", + "no-unreleased-clinical-value", + "no-withheld-resource-existence-or-denial-reason", + "no-internal-routing-pool-or-staffing-detail", + "no-approval-actor-or-internal-correction-target" + ], + "prohibited_inferences": [ + "withdrawn-value", + "why-or-who-retracted-content", + "replacement-exists" + ], + "accessibility": { + "profile": "same-information-and-actions-through-supported-presentation", + "essential_information_in_imagery_permitted": false, + "color_only_state_permitted": false, + "patient_action_loss_permitted": false + }, + "audit": [ + "journey-evaluation-with-policy-and-case-id", + "authorization-and-current-context-outcome", + "disclosed-field-keys-without-values", + "communication-state-transition-without-message-body", + "withhold-correction-retraction-or-outage-reason-code-without-clinical-content" + ], + "offline": { + "phi_cache_permitted": false, + "stale_value_may_be_presented_as_current": false, + "queued_mutation_permitted": false, + "last_known_value_requires_separate_approved_policy": true + }, + "rollback": { + "kill_switch_required": true, + "release_withdrawal_required": true, + "cached_handle_and_volatile_draft_purge_required": true + } + }, + "required_approval_gates": [ + "named-product-and-contract-owner", + "approved-self-identity-session-and-recovery-design", + "approved-current-inpatient-source-and-lifecycle-design", + "approved-operation-specific-authorization-and-nondisclosure-matrix", + "approved-field-source-release-freshness-uncertainty-correction-language-and-offline-matrix", + "approved-clinical-content-and-patient-language", + "approved-communication-routing-replay-urgency-and-patient-visible-state-model", + "approved-representative-consent-scope-expiry-revocation-and-sensitive-service-policy", + "approved-accessibility-language-interpreter-and-accommodation-design", + "approved-privacy-security-threat-and-clinical-hazard-controls", + "approved-audit-monitoring-support-incident-rollback-and-kill-switch-plan", + "canonical-backend-ios-android-fixture-parity", + "default-off-nonproduction-integration-approval", + "approved-cross-surface-retraction-propagation-and-support-policy" + ] + }, + { + "case_id": "incorrect_content_corrected_replacement", + "family_id": "content-retraction-and-correction", + "fixture_class": "synthetic-no-phi", + "execution_status": "held-not-runnable", + "actor": "self", + "encounter_state": "current-inpatient", + "preconditions": [ + "prior-release-withdrawn", + "replacement-content-independently-reviewed-and-released", + "patient-correction-notice-approved" + ], + "event": "corrected-replacement-becomes-effective", + "expected": { + "authorization_result": "eligible-only-after-every-required-gate", + "disclosure": { + "mode": "correction-replacement", + "surfaces": [ + "today", + "my-path", + "care-team", + "communication" + ], + "patient_message_key": "journey.content.corrected", + "values_must_be_separately_released": true, + "atomic_response_required": true, + "no_store_required": true + }, + "state_transitions": [ + "old-value-never-serialized-with-replacement", + "replacement-carries-its-own-complete-field-context", + "source-free-patient-correction-notice-shown" + ], + "communication": { + "compose_state": "not-applicable", + "accepted_state_meaning": "not-applicable", + "routing_state": "not-applicable", + "notification_state": "not-available", + "volatile_draft_action": "clear-on-inactive-or-context-loss" + }, + "nondisclosure": [ + "no-source-system-identifier", + "no-patient-principal-grant-encounter-or-staff-identifier", + "no-unreleased-clinical-value", + "no-withheld-resource-existence-or-denial-reason", + "no-internal-routing-pool-or-staffing-detail", + "no-approval-actor-or-internal-correction-target" + ], + "prohibited_inferences": [ + "internal-correction-reason-or-actor", + "replacement-is-a-complete-clinical-explanation" + ], + "accessibility": { + "profile": "same-information-and-actions-through-supported-presentation", + "essential_information_in_imagery_permitted": false, + "color_only_state_permitted": false, + "patient_action_loss_permitted": false + }, + "audit": [ + "journey-evaluation-with-policy-and-case-id", + "authorization-and-current-context-outcome", + "disclosed-field-keys-without-values", + "communication-state-transition-without-message-body", + "withhold-correction-retraction-or-outage-reason-code-without-clinical-content" + ], + "offline": { + "phi_cache_permitted": false, + "stale_value_may_be_presented_as_current": false, + "queued_mutation_permitted": false, + "last_known_value_requires_separate_approved_policy": true + }, + "rollback": { + "kill_switch_required": true, + "release_withdrawal_required": true, + "cached_handle_and_volatile_draft_purge_required": true + } + }, + "required_approval_gates": [ + "named-product-and-contract-owner", + "approved-self-identity-session-and-recovery-design", + "approved-current-inpatient-source-and-lifecycle-design", + "approved-operation-specific-authorization-and-nondisclosure-matrix", + "approved-field-source-release-freshness-uncertainty-correction-language-and-offline-matrix", + "approved-clinical-content-and-patient-language", + "approved-communication-routing-replay-urgency-and-patient-visible-state-model", + "approved-representative-consent-scope-expiry-revocation-and-sensitive-service-policy", + "approved-accessibility-language-interpreter-and-accommodation-design", + "approved-privacy-security-threat-and-clinical-hazard-controls", + "approved-audit-monitoring-support-incident-rollback-and-kill-switch-plan", + "canonical-backend-ios-android-fixture-parity", + "default-off-nonproduction-integration-approval", + "approved-cross-surface-correction-review-release-and-notice-policy" + ] + } + ] +} diff --git a/docs/nightingale/api-contract/candidates/today/v0/candidate.json b/docs/nightingale/api-contract/candidates/today/v0/candidate.json new file mode 100644 index 00000000..b30128c6 --- /dev/null +++ b/docs/nightingale/api-contract/candidates/today/v0/candidate.json @@ -0,0 +1,379 @@ +{ + "artifact_kind": "nightingale-nonrunnable-api-candidate", + "candidate_id": "nightingale.today-projection.v0-candidate", + "status": "held-no-operation", + "purpose": "Describe one patient-safe Today projection using field-level governance without adding an API operation, route, provider, client, source query, patient, or activation.", + "operation": { + "method": "GET", + "route_namespace": "/api/nightingale/v1", + "path": "/inpatient-contexts/{encounter_handle}/today", + "operation_id": "getNightingaleTodayProjection", + "openapi_inclusion": false, + "route_registration_permitted": false, + "client_generation_permitted": false, + "network_client_permitted": false, + "legacy_alias_permitted": false + }, + "activation": { + "product": false, + "operation": false, + "identity": false, + "inpatient_source": false, + "projection_source": false, + "clinical_content_release": false, + "localization": false, + "disclosure": false, + "native_clients": false, + "nonproduction_integration": false, + "production": false + }, + "request": { + "path_parameter": "encounter_handle", + "encounter_handle_pattern": "^ntg_enc_[a-z2-7]{50}$", + "query_parameters": [], + "request_body_permitted": false, + "source_identifier_input_permitted": false, + "legacy_identifier_input_permitted": false, + "durable_client_storage_permitted": false + }, + "audience": { + "permitted_relationships": [ + "self" + ], + "representative_access": "held", + "sensitive_encounter_access": "held" + }, + "response_contract": { + "envelope_fields": [ + "data", + "meta", + "links" + ], + "data_fields": [ + "kind", + "content_revision", + "sections", + "revision_notice" + ], + "kind": "today", + "content_revision_pattern": "^ntg_tdyrev_[a-z2-7]{50}$", + "schedule_item_handle_pattern": "^ntg_tdyitem_[a-z2-7]{50}$", + "section_names": [ + "headline", + "summary", + "schedule", + "next_steps", + "care_location", + "discharge_outlook", + "questions", + "notices" + ], + "mandatory_released_sections": [ + "headline", + "summary" + ], + "optional_section_states": [ + "released", + "released-empty", + "not-available" + ], + "governed_value_fields": [ + "value", + "context" + ], + "field_context_fields": [ + "release", + "freshness", + "uncertainty", + "language", + "correction", + "offline" + ], + "root_aggregate_provenance_permitted": false, + "root_aggregate_freshness_permitted": false, + "root_aggregate_uncertainty_permitted": false, + "patient_visible_value_without_field_context_permitted": false, + "released_empty_means_no_care_planned": false, + "not_available_means_no_care_planned": false, + "durable_client_cache_permitted": false, + "cache_control": "private, no-store, max-age=0" + }, + "field_context": { + "release_fields": [ + "state", + "released_at", + "content_policy_version" + ], + "release_states": [ + "released" + ], + "freshness_fields": [ + "status", + "observed_at", + "patient_notice" + ], + "freshness_states": [ + "current", + "approved-stale" + ], + "approved_stale_requires_patient_notice": true, + "uncertainty_fields": [ + "level", + "explanation", + "can_change" + ], + "uncertainty_levels": [ + "low", + "medium", + "high", + "unknown" + ], + "language_fields": [ + "locale", + "release_state", + "plain_language_review" + ], + "language_release_states": [ + "approved-source-language", + "approved-translation" + ], + "plain_language_review_states": [ + "approved" + ], + "correction_fields": [ + "state", + "patient_notice" + ], + "correction_states": [ + "original", + "corrected" + ], + "corrected_requires_patient_notice": true, + "offline_fields": [ + "availability", + "durable_storage_permitted" + ], + "offline_availability": [ + "online-only" + ], + "offline_durable_storage_permitted": false + }, + "content_rules": { + "schedule_statuses": [ + "requested", + "planned", + "confirmed", + "in-progress", + "completed", + "delayed", + "canceled" + ], + "timing_confidence": [ + "confirmed", + "estimated", + "unknown" + ], + "care_location_statuses": [ + "current", + "updating", + "unknown" + ], + "correction_notice_kinds": [ + "correction" + ], + "exact_time_or_eta_claim_permitted": false, + "raw_source_text_permitted": false, + "staff_only_text_permitted": false, + "internal_identifier_permitted": false, + "field_level_locale_required": true, + "field_level_release_required": true, + "field_level_freshness_required": true, + "field_level_uncertainty_required": true, + "field_level_correction_required": true, + "field_level_offline_rule_required": true + }, + "authorization_gates": [ + "approved-operation-in-active-contract", + "product-operation-facility-cohort-default-off-gates", + "approved-nightingale-realm-principal-session-and-assurance", + "verified-unmerged-self-identity-link", + "opaque-context-handle-owned-by-principal", + "authoritative-current-inpatient-context", + "operation-specific-server-side-today-capability", + "effective-governed-release-policy", + "effective-patient-language-release", + "field-level-freshness-and-uncertainty-decision", + "field-level-localization-and-correction-decision", + "authorization-and-context-recheck-before-serialization", + "durable-request-and-field-disclosure-audit", + "atomic-no-store-patient-safe-response" + ], + "failure_codes": { + "not_found": 404, + "authentication_required": 401, + "access_unavailable": 403, + "account_state_requires_review": 409, + "rate_limited": 429, + "temporarily_unavailable": 503 + }, + "audit": { + "request_event": "nightingale.today.evaluated", + "disclosure_event": "nightingale.today.fields_disclosed", + "success_requires_durable_request_audit": true, + "success_requires_durable_field_disclosure_audit": true, + "raw_patient_value_recording_permitted": false, + "raw_handle_recording_permitted": false, + "source_identifier_recording_permitted": false, + "staff_actor_recording_permitted": false, + "free_text_recording_permitted": false + }, + "evidence": { + "product_universe_source_count": 256, + "product_universe_inventory_digest": "a307e1957df7ef78eb61a9a9123f3902fd8929ebb3aaeb4dce48f2c88fb4a881", + "classification_ids": [ + "nightingale-source-classification.v1", + "nightingale-journey-preference-presentation-release-source-classification.v1" + ], + "direct_sources": [ + { + "path": "docs/hummingbird/api-contract/hummingbird-patient.v1.yaml", + "sha256": "fb6220b4ef8eb106223624a9785256fdc1603995f281f430a79897905cb45a1b" + }, + { + "path": "app/Http/Controllers/Api/Patient/EncounterProjectionController.php", + "sha256": "8d55765655a35ea87d809c5bf964a4e8b25ecb6a25081788d05319bbbfbacaf0" + }, + { + "path": "app/Services/Patient/Projection/PatientProjectionDisclosureService.php", + "sha256": "9d5b5dfea90985f022b190baee18eb2826cf395b67c936576e50ef25fa418049" + }, + { + "path": "app/Services/Patient/Projection/PatientProjectionContentGuard.php", + "sha256": "55c1ef8f34750f4831eb98dd519824ba61344e2fe56a94c6c855c2db90772d9a" + }, + { + "path": "app/Policies/Patient/PatientEncounterProjectionPolicy.php", + "sha256": "582267c8e7aff6d8e68574319907654fcf3111fb93834221cfffc48943362209" + }, + { + "path": "app/Models/Patient/PatientEncounterProjection.php", + "sha256": "b2675fea1fa6d2a3362ce54bb732858da221604f1f388b766d8fea1f6f449eb0" + }, + { + "path": "database/migrations/2026_07_19_000200_create_patient_experience_projection_kernel.php", + "sha256": "20c55f5d563bfd3342ec4e833d71dd32b4f04c49fe1c455a96b05a03aae257ef" + }, + { + "path": "tests/Feature/Patient/PatientProjectionApiTest.php", + "sha256": "3775071196794e2856f05e35eeac86f7dd6ea1b074dd08549318416d171a9e2e" + }, + { + "path": "hummingbird/iosPatientApp/HummingbirdPatient/Networking/PatientAPIModels.swift", + "sha256": "93c3250b7292c9abe17e3bb932e39c489332c8becc321663b4b8365a1e468759" + }, + { + "path": "hummingbird/iosPatientApp/HummingbirdPatient/Features/Today/PatientTodayView.swift", + "sha256": "63f2003a80cc316f4f47d76cc0a1e56b2d3c77875004be78c27d252d0fcae663" + }, + { + "path": "hummingbird/iosPatientApp/HummingbirdPatient/App/PatientAppViewModel.swift", + "sha256": "74918da1c9af1a263b4535dec933c4c6acf4384bb7afa195997a26e5e1c37694" + }, + { + "path": "hummingbird/androidPatientApp/app/src/main/java/net/acumenus/hummingbird/patient/data/PatientApiModels.kt", + "sha256": "a1932c3dc2f037f4006c72e083d57bc6219f63de42f348c7909737707d25e115" + }, + { + "path": "hummingbird/androidPatientApp/app/src/main/java/net/acumenus/hummingbird/patient/data/PatientSessionCoordinator.kt", + "sha256": "10bfbe8782746390ea71242d1c95ff5f09809b61be40533f91cba1e9acfaada2" + }, + { + "path": "hummingbird/androidPatientApp/app/src/main/java/net/acumenus/hummingbird/patient/ui/PatientExperienceScreen.kt", + "sha256": "7b2345a9735451514aec54fe52bf04d28964935c902bb1b13676e76e8065911d" + } + ] + }, + "required_fixture_case_ids": [ + "current_full", + "current_minimal", + "schedule_released_empty", + "optional_sections_not_available", + "approved_stale_with_notice", + "corrected_replacement", + "approved_translation", + "approved_source_language", + "delayed_schedule_high_uncertainty", + "discharge_outlook_unknown_timing", + "care_location_updating", + "distinct_field_contexts", + "product_disabled", + "operation_disabled", + "facility_disabled", + "cohort_ineligible", + "authentication_missing", + "wrong_identity_realm", + "principal_inactive", + "session_missing", + "session_expired", + "session_revoked", + "identity_link_missing", + "identity_link_revoked", + "wrong_principal_handle", + "unknown_handle", + "malformed_handle", + "representative_relationship_held", + "sensitive_encounter_held", + "current_context_closed", + "current_context_changed", + "handle_mapping_inconsistent", + "source_unavailable", + "database_unavailable", + "today_capability_denied", + "no_released_projection", + "draft_projection", + "future_release", + "retracted_projection", + "correction_without_replacement", + "conflicting_active_releases", + "policy_version_mismatch", + "clinical_approval_missing", + "patient_language_approval_missing", + "translation_approval_missing", + "locale_mismatch", + "freshness_decision_missing", + "unapproved_stale_content", + "unknown_freshness", + "mandatory_headline_missing", + "mandatory_summary_missing", + "ungoverned_field", + "aggregate_context_only", + "internal_identifier_present", + "staff_only_content_present", + "invalid_schedule_status", + "invalid_timing_confidence", + "invalid_item_handle", + "duplicate_item_handle", + "invalid_section_state", + "released_empty_with_items", + "not_available_with_content", + "inconsistent_timestamps", + "correction_actor_exposed", + "request_audit_unavailable", + "disclosure_audit_unavailable", + "response_serialization_failure", + "rate_limited" + ], + "approval_gates": [ + "named-product-contract-backend-identity-source-clinical-content-language-privacy-security-accessibility-support-operations-and-release-owners", + "approved-identity-session-recovery-and-self-link-design", + "approved-current-inpatient-source-cohort-lifecycle-freshness-and-linkage-design", + "approved-field-source-release-freshness-uncertainty-correction-translation-and-offline-matrix", + "approved-patient-state-and-timing-vocabulary", + "approved-operation-specific-authorization-and-nondisclosure-tests", + "approved-content-generation-review-release-correction-and-retraction-workflow", + "canonical-backend-ios-android-fixture-parity", + "threat-model-and-privacy-security-review", + "patient-advisor-accessibility-language-and-support-review", + "default-off-nonproduction-integration-and-rollback" + ] +} diff --git a/docs/nightingale/api-contract/candidates/today/v0/fixtures.json b/docs/nightingale/api-contract/candidates/today/v0/fixtures.json new file mode 100644 index 00000000..25960d47 --- /dev/null +++ b/docs/nightingale/api-contract/candidates/today/v0/fixtures.json @@ -0,0 +1,8945 @@ +{ + "fixture_schema": "nightingale.today-projection.candidate-fixtures.v0", + "candidate_id": "nightingale.today-projection.v0-candidate", + "synthetic_only": true, + "production_replay_permitted": false, + "contains_real_patient_data": false, + "request_template": { + "method": "GET", + "route_namespace": "/api/nightingale/v1", + "path": "/inpatient-contexts/{encounter_handle}/today", + "path_parameters": { + "encounter_handle": "ntg_enc_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + }, + "query_parameters": {}, + "body": null, + "authentication_context": "approved-nightingale-session-required" + }, + "response_templates": { + "success_full_current": { + "status": 200, + "headers": { + "Cache-Control": "private, no-store, max-age=0", + "Pragma": "no-cache", + "X-Robots-Tag": "noindex, nofollow, noarchive", + "Vary": "Authorization, Accept-Language", + "Content-Type": "application/json" + }, + "body": { + "data": { + "kind": "today", + "content_revision": "ntg_tdyrev_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "sections": { + "headline": { + "state": "released", + "field": { + "value": "Your care plan for today", + "context": { + "release": { + "state": "released", + "released_at": "2026-07-26T14:58:00Z", + "content_policy_version": "nightingale-patient-language.v0-candidate" + }, + "freshness": { + "status": "current", + "observed_at": "2026-07-26T14:55:00Z", + "patient_notice": null + }, + "uncertainty": { + "level": "low", + "explanation": "This is the latest released information available for this item.", + "can_change": true + }, + "language": { + "locale": "en-US", + "release_state": "approved-source-language", + "plain_language_review": "approved" + }, + "correction": { + "state": "original", + "patient_notice": null + }, + "offline": { + "availability": "online-only", + "durable_storage_permitted": false + } + } + } + }, + "summary": { + "state": "released", + "field": { + "value": "Your care team has released the following information for today.", + "context": { + "release": { + "state": "released", + "released_at": "2026-07-26T14:58:00Z", + "content_policy_version": "nightingale-patient-language.v0-candidate" + }, + "freshness": { + "status": "current", + "observed_at": "2026-07-26T14:55:00Z", + "patient_notice": null + }, + "uncertainty": { + "level": "low", + "explanation": "This is the latest released information available for this item.", + "can_change": true + }, + "language": { + "locale": "en-US", + "release_state": "approved-source-language", + "plain_language_review": "approved" + }, + "correction": { + "state": "original", + "patient_notice": null + }, + "offline": { + "availability": "online-only", + "durable_storage_permitted": false + } + } + } + }, + "schedule": { + "state": "released", + "items": [ + { + "item_handle": "ntg_tdyitem_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "label": { + "value": "Morning test", + "context": { + "release": { + "state": "released", + "released_at": "2026-07-26T14:58:00Z", + "content_policy_version": "nightingale-patient-language.v0-candidate" + }, + "freshness": { + "status": "current", + "observed_at": "2026-07-26T14:55:00Z", + "patient_notice": null + }, + "uncertainty": { + "level": "low", + "explanation": "This is the latest released information available for this item.", + "can_change": true + }, + "language": { + "locale": "en-US", + "release_state": "approved-source-language", + "plain_language_review": "approved" + }, + "correction": { + "state": "original", + "patient_notice": null + }, + "offline": { + "availability": "online-only", + "durable_storage_permitted": false + } + } + }, + "detail": { + "value": "Your care team will explain what to expect before the test.", + "context": { + "release": { + "state": "released", + "released_at": "2026-07-26T14:58:00Z", + "content_policy_version": "nightingale-patient-language.v0-candidate" + }, + "freshness": { + "status": "current", + "observed_at": "2026-07-26T14:55:00Z", + "patient_notice": null + }, + "uncertainty": { + "level": "low", + "explanation": "This is the latest released information available for this item.", + "can_change": true + }, + "language": { + "locale": "en-US", + "release_state": "approved-source-language", + "plain_language_review": "approved" + }, + "correction": { + "state": "original", + "patient_notice": null + }, + "offline": { + "availability": "online-only", + "durable_storage_permitted": false + } + } + }, + "status": { + "value": "planned", + "context": { + "release": { + "state": "released", + "released_at": "2026-07-26T14:58:00Z", + "content_policy_version": "nightingale-patient-language.v0-candidate" + }, + "freshness": { + "status": "current", + "observed_at": "2026-07-26T14:55:00Z", + "patient_notice": null + }, + "uncertainty": { + "level": "low", + "explanation": "This is the latest released information available for this item.", + "can_change": true + }, + "language": { + "locale": "en-US", + "release_state": "approved-source-language", + "plain_language_review": "approved" + }, + "correction": { + "state": "original", + "patient_notice": null + }, + "offline": { + "availability": "online-only", + "durable_storage_permitted": false + } + } + }, + "time_window": { + "value": "This morning", + "context": { + "release": { + "state": "released", + "released_at": "2026-07-26T14:58:00Z", + "content_policy_version": "nightingale-patient-language.v0-candidate" + }, + "freshness": { + "status": "current", + "observed_at": "2026-07-26T14:55:00Z", + "patient_notice": null + }, + "uncertainty": { + "level": "medium", + "explanation": "This information may change as your care team learns more.", + "can_change": true + }, + "language": { + "locale": "en-US", + "release_state": "approved-source-language", + "plain_language_review": "approved" + }, + "correction": { + "state": "original", + "patient_notice": null + }, + "offline": { + "availability": "online-only", + "durable_storage_permitted": false + } + } + }, + "timing_confidence": { + "value": "estimated", + "context": { + "release": { + "state": "released", + "released_at": "2026-07-26T14:58:00Z", + "content_policy_version": "nightingale-patient-language.v0-candidate" + }, + "freshness": { + "status": "current", + "observed_at": "2026-07-26T14:55:00Z", + "patient_notice": null + }, + "uncertainty": { + "level": "medium", + "explanation": "This information may change as your care team learns more.", + "can_change": true + }, + "language": { + "locale": "en-US", + "release_state": "approved-source-language", + "plain_language_review": "approved" + }, + "correction": { + "state": "original", + "patient_notice": null + }, + "offline": { + "availability": "online-only", + "durable_storage_permitted": false + } + } + }, + "preparation": { + "value": "Please ask your nurse whether you need to prepare.", + "context": { + "release": { + "state": "released", + "released_at": "2026-07-26T14:58:00Z", + "content_policy_version": "nightingale-patient-language.v0-candidate" + }, + "freshness": { + "status": "current", + "observed_at": "2026-07-26T14:55:00Z", + "patient_notice": null + }, + "uncertainty": { + "level": "medium", + "explanation": "This information may change as your care team learns more.", + "can_change": true + }, + "language": { + "locale": "en-US", + "release_state": "approved-source-language", + "plain_language_review": "approved" + }, + "correction": { + "state": "original", + "patient_notice": null + }, + "offline": { + "availability": "online-only", + "durable_storage_permitted": false + } + } + }, + "can_change": { + "value": true, + "context": { + "release": { + "state": "released", + "released_at": "2026-07-26T14:58:00Z", + "content_policy_version": "nightingale-patient-language.v0-candidate" + }, + "freshness": { + "status": "current", + "observed_at": "2026-07-26T14:55:00Z", + "patient_notice": null + }, + "uncertainty": { + "level": "medium", + "explanation": "This information may change as your care team learns more.", + "can_change": true + }, + "language": { + "locale": "en-US", + "release_state": "approved-source-language", + "plain_language_review": "approved" + }, + "correction": { + "state": "original", + "patient_notice": null + }, + "offline": { + "availability": "online-only", + "durable_storage_permitted": false + } + } + } + } + ], + "patient_notice": null + }, + "next_steps": { + "state": "released", + "items": [ + { + "value": "Talk with your care team during rounds.", + "context": { + "release": { + "state": "released", + "released_at": "2026-07-26T14:58:00Z", + "content_policy_version": "nightingale-patient-language.v0-candidate" + }, + "freshness": { + "status": "current", + "observed_at": "2026-07-26T14:55:00Z", + "patient_notice": null + }, + "uncertainty": { + "level": "low", + "explanation": "This is the latest released information available for this item.", + "can_change": true + }, + "language": { + "locale": "en-US", + "release_state": "approved-source-language", + "plain_language_review": "approved" + }, + "correction": { + "state": "original", + "patient_notice": null + }, + "offline": { + "availability": "online-only", + "durable_storage_permitted": false + } + } + } + ], + "patient_notice": null + }, + "care_location": { + "state": "released", + "fields": { + "facility_display_name": { + "value": "Acumenus Medical Center", + "context": { + "release": { + "state": "released", + "released_at": "2026-07-26T14:58:00Z", + "content_policy_version": "nightingale-patient-language.v0-candidate" + }, + "freshness": { + "status": "current", + "observed_at": "2026-07-26T14:55:00Z", + "patient_notice": null + }, + "uncertainty": { + "level": "low", + "explanation": "This is the latest released information available for this item.", + "can_change": true + }, + "language": { + "locale": "en-US", + "release_state": "approved-source-language", + "plain_language_review": "approved" + }, + "correction": { + "state": "original", + "patient_notice": null + }, + "offline": { + "availability": "online-only", + "durable_storage_permitted": false + } + } + }, + "unit_display_name": { + "value": "Medical unit", + "context": { + "release": { + "state": "released", + "released_at": "2026-07-26T14:58:00Z", + "content_policy_version": "nightingale-patient-language.v0-candidate" + }, + "freshness": { + "status": "current", + "observed_at": "2026-07-26T14:55:00Z", + "patient_notice": null + }, + "uncertainty": { + "level": "low", + "explanation": "This is the latest released information available for this item.", + "can_change": true + }, + "language": { + "locale": "en-US", + "release_state": "approved-source-language", + "plain_language_review": "approved" + }, + "correction": { + "state": "original", + "patient_notice": null + }, + "offline": { + "availability": "online-only", + "durable_storage_permitted": false + } + } + }, + "room_display_name": { + "value": "Your current room", + "context": { + "release": { + "state": "released", + "released_at": "2026-07-26T14:58:00Z", + "content_policy_version": "nightingale-patient-language.v0-candidate" + }, + "freshness": { + "status": "current", + "observed_at": "2026-07-26T14:55:00Z", + "patient_notice": null + }, + "uncertainty": { + "level": "low", + "explanation": "This is the latest released information available for this item.", + "can_change": true + }, + "language": { + "locale": "en-US", + "release_state": "approved-source-language", + "plain_language_review": "approved" + }, + "correction": { + "state": "original", + "patient_notice": null + }, + "offline": { + "availability": "online-only", + "durable_storage_permitted": false + } + } + }, + "status": { + "value": "current", + "context": { + "release": { + "state": "released", + "released_at": "2026-07-26T14:58:00Z", + "content_policy_version": "nightingale-patient-language.v0-candidate" + }, + "freshness": { + "status": "current", + "observed_at": "2026-07-26T14:55:00Z", + "patient_notice": null + }, + "uncertainty": { + "level": "low", + "explanation": "This is the latest released information available for this item.", + "can_change": true + }, + "language": { + "locale": "en-US", + "release_state": "approved-source-language", + "plain_language_review": "approved" + }, + "correction": { + "state": "original", + "patient_notice": null + }, + "offline": { + "availability": "online-only", + "durable_storage_permitted": false + } + } + } + }, + "patient_notice": null + }, + "discharge_outlook": { + "state": "released", + "fields": { + "estimated_range": { + "value": "Your care team is still reviewing timing.", + "context": { + "release": { + "state": "released", + "released_at": "2026-07-26T14:58:00Z", + "content_policy_version": "nightingale-patient-language.v0-candidate" + }, + "freshness": { + "status": "current", + "observed_at": "2026-07-26T14:55:00Z", + "patient_notice": null + }, + "uncertainty": { + "level": "unknown", + "explanation": "This information may change as your care team learns more.", + "can_change": true + }, + "language": { + "locale": "en-US", + "release_state": "approved-source-language", + "plain_language_review": "approved" + }, + "correction": { + "state": "original", + "patient_notice": null + }, + "offline": { + "availability": "online-only", + "durable_storage_permitted": false + } + } + }, + "confidence": { + "value": "unknown", + "context": { + "release": { + "state": "released", + "released_at": "2026-07-26T14:58:00Z", + "content_policy_version": "nightingale-patient-language.v0-candidate" + }, + "freshness": { + "status": "current", + "observed_at": "2026-07-26T14:55:00Z", + "patient_notice": null + }, + "uncertainty": { + "level": "unknown", + "explanation": "This information may change as your care team learns more.", + "can_change": true + }, + "language": { + "locale": "en-US", + "release_state": "approved-source-language", + "plain_language_review": "approved" + }, + "correction": { + "state": "original", + "patient_notice": null + }, + "offline": { + "availability": "online-only", + "durable_storage_permitted": false + } + } + }, + "readiness_topics": { + "state": "released", + "items": [ + { + "value": "Safe movement and support at home.", + "context": { + "release": { + "state": "released", + "released_at": "2026-07-26T14:58:00Z", + "content_policy_version": "nightingale-patient-language.v0-candidate" + }, + "freshness": { + "status": "current", + "observed_at": "2026-07-26T14:55:00Z", + "patient_notice": null + }, + "uncertainty": { + "level": "low", + "explanation": "This is the latest released information available for this item.", + "can_change": true + }, + "language": { + "locale": "en-US", + "release_state": "approved-source-language", + "plain_language_review": "approved" + }, + "correction": { + "state": "original", + "patient_notice": null + }, + "offline": { + "availability": "online-only", + "durable_storage_permitted": false + } + } + } + ], + "patient_notice": null + }, + "remaining_steps": { + "state": "released", + "items": [ + { + "value": "Your team will review what needs to happen next.", + "context": { + "release": { + "state": "released", + "released_at": "2026-07-26T14:58:00Z", + "content_policy_version": "nightingale-patient-language.v0-candidate" + }, + "freshness": { + "status": "current", + "observed_at": "2026-07-26T14:55:00Z", + "patient_notice": null + }, + "uncertainty": { + "level": "low", + "explanation": "This is the latest released information available for this item.", + "can_change": true + }, + "language": { + "locale": "en-US", + "release_state": "approved-source-language", + "plain_language_review": "approved" + }, + "correction": { + "state": "original", + "patient_notice": null + }, + "offline": { + "availability": "online-only", + "durable_storage_permitted": false + } + } + } + ], + "patient_notice": null + }, + "can_change": { + "value": true, + "context": { + "release": { + "state": "released", + "released_at": "2026-07-26T14:58:00Z", + "content_policy_version": "nightingale-patient-language.v0-candidate" + }, + "freshness": { + "status": "current", + "observed_at": "2026-07-26T14:55:00Z", + "patient_notice": null + }, + "uncertainty": { + "level": "unknown", + "explanation": "This information may change as your care team learns more.", + "can_change": true + }, + "language": { + "locale": "en-US", + "release_state": "approved-source-language", + "plain_language_review": "approved" + }, + "correction": { + "state": "original", + "patient_notice": null + }, + "offline": { + "availability": "online-only", + "durable_storage_permitted": false + } + } + } + }, + "patient_notice": "This is not a promised discharge date. Timing can change." + }, + "questions": { + "state": "released", + "items": [ + { + "value": "What should I expect before my next planned step?", + "context": { + "release": { + "state": "released", + "released_at": "2026-07-26T14:58:00Z", + "content_policy_version": "nightingale-patient-language.v0-candidate" + }, + "freshness": { + "status": "current", + "observed_at": "2026-07-26T14:55:00Z", + "patient_notice": null + }, + "uncertainty": { + "level": "low", + "explanation": "This is the latest released information available for this item.", + "can_change": true + }, + "language": { + "locale": "en-US", + "release_state": "approved-source-language", + "plain_language_review": "approved" + }, + "correction": { + "state": "original", + "patient_notice": null + }, + "offline": { + "availability": "online-only", + "durable_storage_permitted": false + } + } + } + ], + "patient_notice": null + }, + "notices": { + "state": "released", + "items": [ + { + "value": "Care plans can change. Ask your care team if this does not match what you were told.", + "context": { + "release": { + "state": "released", + "released_at": "2026-07-26T14:58:00Z", + "content_policy_version": "nightingale-patient-language.v0-candidate" + }, + "freshness": { + "status": "current", + "observed_at": "2026-07-26T14:55:00Z", + "patient_notice": null + }, + "uncertainty": { + "level": "low", + "explanation": "This is the latest released information available for this item.", + "can_change": true + }, + "language": { + "locale": "en-US", + "release_state": "approved-source-language", + "plain_language_review": "approved" + }, + "correction": { + "state": "original", + "patient_notice": null + }, + "offline": { + "availability": "online-only", + "durable_storage_permitted": false + } + } + } + ], + "patient_notice": null + } + }, + "revision_notice": null + }, + "meta": { + "request_id": "ntg_req_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "generated_at": "2026-07-26T15:00:00Z", + "policy_version": "nightingale-today-policy.v0-candidate", + "authorization_evaluated_at": "2026-07-26T15:00:00Z", + "inpatient_context_evaluated_at": "2026-07-26T15:00:00Z", + "locale": "en-US", + "completeness": "governed-evaluation-complete" + }, + "links": {} + } + }, + "success_minimal": { + "status": 200, + "headers": { + "Cache-Control": "private, no-store, max-age=0", + "Pragma": "no-cache", + "X-Robots-Tag": "noindex, nofollow, noarchive", + "Vary": "Authorization, Accept-Language", + "Content-Type": "application/json" + }, + "body": { + "data": { + "kind": "today", + "content_revision": "ntg_tdyrev_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "sections": { + "headline": { + "state": "released", + "field": { + "value": "Your care plan for today", + "context": { + "release": { + "state": "released", + "released_at": "2026-07-26T14:58:00Z", + "content_policy_version": "nightingale-patient-language.v0-candidate" + }, + "freshness": { + "status": "current", + "observed_at": "2026-07-26T14:55:00Z", + "patient_notice": null + }, + "uncertainty": { + "level": "low", + "explanation": "This is the latest released information available for this item.", + "can_change": true + }, + "language": { + "locale": "en-US", + "release_state": "approved-source-language", + "plain_language_review": "approved" + }, + "correction": { + "state": "original", + "patient_notice": null + }, + "offline": { + "availability": "online-only", + "durable_storage_permitted": false + } + } + } + }, + "summary": { + "state": "released", + "field": { + "value": "Your care team has released the following information for today.", + "context": { + "release": { + "state": "released", + "released_at": "2026-07-26T14:58:00Z", + "content_policy_version": "nightingale-patient-language.v0-candidate" + }, + "freshness": { + "status": "current", + "observed_at": "2026-07-26T14:55:00Z", + "patient_notice": null + }, + "uncertainty": { + "level": "low", + "explanation": "This is the latest released information available for this item.", + "can_change": true + }, + "language": { + "locale": "en-US", + "release_state": "approved-source-language", + "plain_language_review": "approved" + }, + "correction": { + "state": "original", + "patient_notice": null + }, + "offline": { + "availability": "online-only", + "durable_storage_permitted": false + } + } + } + }, + "schedule": { + "state": "not-available", + "patient_notice": "This part of today’s plan is not available here. Ask your care team for the current information." + }, + "next_steps": { + "state": "not-available", + "patient_notice": "This part of today’s plan is not available here. Ask your care team for the current information." + }, + "care_location": { + "state": "not-available", + "patient_notice": "This part of today’s plan is not available here. Ask your care team for the current information." + }, + "discharge_outlook": { + "state": "not-available", + "patient_notice": "This part of today’s plan is not available here. Ask your care team for the current information." + }, + "questions": { + "state": "not-available", + "patient_notice": "This part of today’s plan is not available here. Ask your care team for the current information." + }, + "notices": { + "state": "not-available", + "patient_notice": "This part of today’s plan is not available here. Ask your care team for the current information." + } + }, + "revision_notice": null + }, + "meta": { + "request_id": "ntg_req_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "generated_at": "2026-07-26T15:00:00Z", + "policy_version": "nightingale-today-policy.v0-candidate", + "authorization_evaluated_at": "2026-07-26T15:00:00Z", + "inpatient_context_evaluated_at": "2026-07-26T15:00:00Z", + "locale": "en-US", + "completeness": "governed-evaluation-complete" + }, + "links": {} + } + }, + "success_schedule_empty": { + "status": 200, + "headers": { + "Cache-Control": "private, no-store, max-age=0", + "Pragma": "no-cache", + "X-Robots-Tag": "noindex, nofollow, noarchive", + "Vary": "Authorization, Accept-Language", + "Content-Type": "application/json" + }, + "body": { + "data": { + "kind": "today", + "content_revision": "ntg_tdyrev_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "sections": { + "headline": { + "state": "released", + "field": { + "value": "Your care plan for today", + "context": { + "release": { + "state": "released", + "released_at": "2026-07-26T14:58:00Z", + "content_policy_version": "nightingale-patient-language.v0-candidate" + }, + "freshness": { + "status": "current", + "observed_at": "2026-07-26T14:55:00Z", + "patient_notice": null + }, + "uncertainty": { + "level": "low", + "explanation": "This is the latest released information available for this item.", + "can_change": true + }, + "language": { + "locale": "en-US", + "release_state": "approved-source-language", + "plain_language_review": "approved" + }, + "correction": { + "state": "original", + "patient_notice": null + }, + "offline": { + "availability": "online-only", + "durable_storage_permitted": false + } + } + } + }, + "summary": { + "state": "released", + "field": { + "value": "Your care team has released the following information for today.", + "context": { + "release": { + "state": "released", + "released_at": "2026-07-26T14:58:00Z", + "content_policy_version": "nightingale-patient-language.v0-candidate" + }, + "freshness": { + "status": "current", + "observed_at": "2026-07-26T14:55:00Z", + "patient_notice": null + }, + "uncertainty": { + "level": "low", + "explanation": "This is the latest released information available for this item.", + "can_change": true + }, + "language": { + "locale": "en-US", + "release_state": "approved-source-language", + "plain_language_review": "approved" + }, + "correction": { + "state": "original", + "patient_notice": null + }, + "offline": { + "availability": "online-only", + "durable_storage_permitted": false + } + } + } + }, + "schedule": { + "state": "released-empty", + "items": [], + "patient_notice": "No items have been released for this section. This does not mean that no care is planned." + }, + "next_steps": { + "state": "released", + "items": [ + { + "value": "Talk with your care team during rounds.", + "context": { + "release": { + "state": "released", + "released_at": "2026-07-26T14:58:00Z", + "content_policy_version": "nightingale-patient-language.v0-candidate" + }, + "freshness": { + "status": "current", + "observed_at": "2026-07-26T14:55:00Z", + "patient_notice": null + }, + "uncertainty": { + "level": "low", + "explanation": "This is the latest released information available for this item.", + "can_change": true + }, + "language": { + "locale": "en-US", + "release_state": "approved-source-language", + "plain_language_review": "approved" + }, + "correction": { + "state": "original", + "patient_notice": null + }, + "offline": { + "availability": "online-only", + "durable_storage_permitted": false + } + } + } + ], + "patient_notice": null + }, + "care_location": { + "state": "released", + "fields": { + "facility_display_name": { + "value": "Acumenus Medical Center", + "context": { + "release": { + "state": "released", + "released_at": "2026-07-26T14:58:00Z", + "content_policy_version": "nightingale-patient-language.v0-candidate" + }, + "freshness": { + "status": "current", + "observed_at": "2026-07-26T14:55:00Z", + "patient_notice": null + }, + "uncertainty": { + "level": "low", + "explanation": "This is the latest released information available for this item.", + "can_change": true + }, + "language": { + "locale": "en-US", + "release_state": "approved-source-language", + "plain_language_review": "approved" + }, + "correction": { + "state": "original", + "patient_notice": null + }, + "offline": { + "availability": "online-only", + "durable_storage_permitted": false + } + } + }, + "unit_display_name": { + "value": "Medical unit", + "context": { + "release": { + "state": "released", + "released_at": "2026-07-26T14:58:00Z", + "content_policy_version": "nightingale-patient-language.v0-candidate" + }, + "freshness": { + "status": "current", + "observed_at": "2026-07-26T14:55:00Z", + "patient_notice": null + }, + "uncertainty": { + "level": "low", + "explanation": "This is the latest released information available for this item.", + "can_change": true + }, + "language": { + "locale": "en-US", + "release_state": "approved-source-language", + "plain_language_review": "approved" + }, + "correction": { + "state": "original", + "patient_notice": null + }, + "offline": { + "availability": "online-only", + "durable_storage_permitted": false + } + } + }, + "room_display_name": { + "value": "Your current room", + "context": { + "release": { + "state": "released", + "released_at": "2026-07-26T14:58:00Z", + "content_policy_version": "nightingale-patient-language.v0-candidate" + }, + "freshness": { + "status": "current", + "observed_at": "2026-07-26T14:55:00Z", + "patient_notice": null + }, + "uncertainty": { + "level": "low", + "explanation": "This is the latest released information available for this item.", + "can_change": true + }, + "language": { + "locale": "en-US", + "release_state": "approved-source-language", + "plain_language_review": "approved" + }, + "correction": { + "state": "original", + "patient_notice": null + }, + "offline": { + "availability": "online-only", + "durable_storage_permitted": false + } + } + }, + "status": { + "value": "current", + "context": { + "release": { + "state": "released", + "released_at": "2026-07-26T14:58:00Z", + "content_policy_version": "nightingale-patient-language.v0-candidate" + }, + "freshness": { + "status": "current", + "observed_at": "2026-07-26T14:55:00Z", + "patient_notice": null + }, + "uncertainty": { + "level": "low", + "explanation": "This is the latest released information available for this item.", + "can_change": true + }, + "language": { + "locale": "en-US", + "release_state": "approved-source-language", + "plain_language_review": "approved" + }, + "correction": { + "state": "original", + "patient_notice": null + }, + "offline": { + "availability": "online-only", + "durable_storage_permitted": false + } + } + } + }, + "patient_notice": null + }, + "discharge_outlook": { + "state": "released", + "fields": { + "estimated_range": { + "value": "Your care team is still reviewing timing.", + "context": { + "release": { + "state": "released", + "released_at": "2026-07-26T14:58:00Z", + "content_policy_version": "nightingale-patient-language.v0-candidate" + }, + "freshness": { + "status": "current", + "observed_at": "2026-07-26T14:55:00Z", + "patient_notice": null + }, + "uncertainty": { + "level": "unknown", + "explanation": "This information may change as your care team learns more.", + "can_change": true + }, + "language": { + "locale": "en-US", + "release_state": "approved-source-language", + "plain_language_review": "approved" + }, + "correction": { + "state": "original", + "patient_notice": null + }, + "offline": { + "availability": "online-only", + "durable_storage_permitted": false + } + } + }, + "confidence": { + "value": "unknown", + "context": { + "release": { + "state": "released", + "released_at": "2026-07-26T14:58:00Z", + "content_policy_version": "nightingale-patient-language.v0-candidate" + }, + "freshness": { + "status": "current", + "observed_at": "2026-07-26T14:55:00Z", + "patient_notice": null + }, + "uncertainty": { + "level": "unknown", + "explanation": "This information may change as your care team learns more.", + "can_change": true + }, + "language": { + "locale": "en-US", + "release_state": "approved-source-language", + "plain_language_review": "approved" + }, + "correction": { + "state": "original", + "patient_notice": null + }, + "offline": { + "availability": "online-only", + "durable_storage_permitted": false + } + } + }, + "readiness_topics": { + "state": "released", + "items": [ + { + "value": "Safe movement and support at home.", + "context": { + "release": { + "state": "released", + "released_at": "2026-07-26T14:58:00Z", + "content_policy_version": "nightingale-patient-language.v0-candidate" + }, + "freshness": { + "status": "current", + "observed_at": "2026-07-26T14:55:00Z", + "patient_notice": null + }, + "uncertainty": { + "level": "low", + "explanation": "This is the latest released information available for this item.", + "can_change": true + }, + "language": { + "locale": "en-US", + "release_state": "approved-source-language", + "plain_language_review": "approved" + }, + "correction": { + "state": "original", + "patient_notice": null + }, + "offline": { + "availability": "online-only", + "durable_storage_permitted": false + } + } + } + ], + "patient_notice": null + }, + "remaining_steps": { + "state": "released", + "items": [ + { + "value": "Your team will review what needs to happen next.", + "context": { + "release": { + "state": "released", + "released_at": "2026-07-26T14:58:00Z", + "content_policy_version": "nightingale-patient-language.v0-candidate" + }, + "freshness": { + "status": "current", + "observed_at": "2026-07-26T14:55:00Z", + "patient_notice": null + }, + "uncertainty": { + "level": "low", + "explanation": "This is the latest released information available for this item.", + "can_change": true + }, + "language": { + "locale": "en-US", + "release_state": "approved-source-language", + "plain_language_review": "approved" + }, + "correction": { + "state": "original", + "patient_notice": null + }, + "offline": { + "availability": "online-only", + "durable_storage_permitted": false + } + } + } + ], + "patient_notice": null + }, + "can_change": { + "value": true, + "context": { + "release": { + "state": "released", + "released_at": "2026-07-26T14:58:00Z", + "content_policy_version": "nightingale-patient-language.v0-candidate" + }, + "freshness": { + "status": "current", + "observed_at": "2026-07-26T14:55:00Z", + "patient_notice": null + }, + "uncertainty": { + "level": "unknown", + "explanation": "This information may change as your care team learns more.", + "can_change": true + }, + "language": { + "locale": "en-US", + "release_state": "approved-source-language", + "plain_language_review": "approved" + }, + "correction": { + "state": "original", + "patient_notice": null + }, + "offline": { + "availability": "online-only", + "durable_storage_permitted": false + } + } + } + }, + "patient_notice": "This is not a promised discharge date. Timing can change." + }, + "questions": { + "state": "released", + "items": [ + { + "value": "What should I expect before my next planned step?", + "context": { + "release": { + "state": "released", + "released_at": "2026-07-26T14:58:00Z", + "content_policy_version": "nightingale-patient-language.v0-candidate" + }, + "freshness": { + "status": "current", + "observed_at": "2026-07-26T14:55:00Z", + "patient_notice": null + }, + "uncertainty": { + "level": "low", + "explanation": "This is the latest released information available for this item.", + "can_change": true + }, + "language": { + "locale": "en-US", + "release_state": "approved-source-language", + "plain_language_review": "approved" + }, + "correction": { + "state": "original", + "patient_notice": null + }, + "offline": { + "availability": "online-only", + "durable_storage_permitted": false + } + } + } + ], + "patient_notice": null + }, + "notices": { + "state": "released", + "items": [ + { + "value": "Care plans can change. Ask your care team if this does not match what you were told.", + "context": { + "release": { + "state": "released", + "released_at": "2026-07-26T14:58:00Z", + "content_policy_version": "nightingale-patient-language.v0-candidate" + }, + "freshness": { + "status": "current", + "observed_at": "2026-07-26T14:55:00Z", + "patient_notice": null + }, + "uncertainty": { + "level": "low", + "explanation": "This is the latest released information available for this item.", + "can_change": true + }, + "language": { + "locale": "en-US", + "release_state": "approved-source-language", + "plain_language_review": "approved" + }, + "correction": { + "state": "original", + "patient_notice": null + }, + "offline": { + "availability": "online-only", + "durable_storage_permitted": false + } + } + } + ], + "patient_notice": null + } + }, + "revision_notice": null + }, + "meta": { + "request_id": "ntg_req_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "generated_at": "2026-07-26T15:00:00Z", + "policy_version": "nightingale-today-policy.v0-candidate", + "authorization_evaluated_at": "2026-07-26T15:00:00Z", + "inpatient_context_evaluated_at": "2026-07-26T15:00:00Z", + "locale": "en-US", + "completeness": "governed-evaluation-complete" + }, + "links": {} + } + }, + "success_optional_unavailable": { + "status": 200, + "headers": { + "Cache-Control": "private, no-store, max-age=0", + "Pragma": "no-cache", + "X-Robots-Tag": "noindex, nofollow, noarchive", + "Vary": "Authorization, Accept-Language", + "Content-Type": "application/json" + }, + "body": { + "data": { + "kind": "today", + "content_revision": "ntg_tdyrev_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "sections": { + "headline": { + "state": "released", + "field": { + "value": "Your care plan for today", + "context": { + "release": { + "state": "released", + "released_at": "2026-07-26T14:58:00Z", + "content_policy_version": "nightingale-patient-language.v0-candidate" + }, + "freshness": { + "status": "current", + "observed_at": "2026-07-26T14:55:00Z", + "patient_notice": null + }, + "uncertainty": { + "level": "low", + "explanation": "This is the latest released information available for this item.", + "can_change": true + }, + "language": { + "locale": "en-US", + "release_state": "approved-source-language", + "plain_language_review": "approved" + }, + "correction": { + "state": "original", + "patient_notice": null + }, + "offline": { + "availability": "online-only", + "durable_storage_permitted": false + } + } + } + }, + "summary": { + "state": "released", + "field": { + "value": "Your care team has released the following information for today.", + "context": { + "release": { + "state": "released", + "released_at": "2026-07-26T14:58:00Z", + "content_policy_version": "nightingale-patient-language.v0-candidate" + }, + "freshness": { + "status": "current", + "observed_at": "2026-07-26T14:55:00Z", + "patient_notice": null + }, + "uncertainty": { + "level": "low", + "explanation": "This is the latest released information available for this item.", + "can_change": true + }, + "language": { + "locale": "en-US", + "release_state": "approved-source-language", + "plain_language_review": "approved" + }, + "correction": { + "state": "original", + "patient_notice": null + }, + "offline": { + "availability": "online-only", + "durable_storage_permitted": false + } + } + } + }, + "schedule": { + "state": "released", + "items": [ + { + "item_handle": "ntg_tdyitem_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "label": { + "value": "Morning test", + "context": { + "release": { + "state": "released", + "released_at": "2026-07-26T14:58:00Z", + "content_policy_version": "nightingale-patient-language.v0-candidate" + }, + "freshness": { + "status": "current", + "observed_at": "2026-07-26T14:55:00Z", + "patient_notice": null + }, + "uncertainty": { + "level": "low", + "explanation": "This is the latest released information available for this item.", + "can_change": true + }, + "language": { + "locale": "en-US", + "release_state": "approved-source-language", + "plain_language_review": "approved" + }, + "correction": { + "state": "original", + "patient_notice": null + }, + "offline": { + "availability": "online-only", + "durable_storage_permitted": false + } + } + }, + "detail": { + "value": "Your care team will explain what to expect before the test.", + "context": { + "release": { + "state": "released", + "released_at": "2026-07-26T14:58:00Z", + "content_policy_version": "nightingale-patient-language.v0-candidate" + }, + "freshness": { + "status": "current", + "observed_at": "2026-07-26T14:55:00Z", + "patient_notice": null + }, + "uncertainty": { + "level": "low", + "explanation": "This is the latest released information available for this item.", + "can_change": true + }, + "language": { + "locale": "en-US", + "release_state": "approved-source-language", + "plain_language_review": "approved" + }, + "correction": { + "state": "original", + "patient_notice": null + }, + "offline": { + "availability": "online-only", + "durable_storage_permitted": false + } + } + }, + "status": { + "value": "planned", + "context": { + "release": { + "state": "released", + "released_at": "2026-07-26T14:58:00Z", + "content_policy_version": "nightingale-patient-language.v0-candidate" + }, + "freshness": { + "status": "current", + "observed_at": "2026-07-26T14:55:00Z", + "patient_notice": null + }, + "uncertainty": { + "level": "low", + "explanation": "This is the latest released information available for this item.", + "can_change": true + }, + "language": { + "locale": "en-US", + "release_state": "approved-source-language", + "plain_language_review": "approved" + }, + "correction": { + "state": "original", + "patient_notice": null + }, + "offline": { + "availability": "online-only", + "durable_storage_permitted": false + } + } + }, + "time_window": { + "value": "This morning", + "context": { + "release": { + "state": "released", + "released_at": "2026-07-26T14:58:00Z", + "content_policy_version": "nightingale-patient-language.v0-candidate" + }, + "freshness": { + "status": "current", + "observed_at": "2026-07-26T14:55:00Z", + "patient_notice": null + }, + "uncertainty": { + "level": "medium", + "explanation": "This information may change as your care team learns more.", + "can_change": true + }, + "language": { + "locale": "en-US", + "release_state": "approved-source-language", + "plain_language_review": "approved" + }, + "correction": { + "state": "original", + "patient_notice": null + }, + "offline": { + "availability": "online-only", + "durable_storage_permitted": false + } + } + }, + "timing_confidence": { + "value": "estimated", + "context": { + "release": { + "state": "released", + "released_at": "2026-07-26T14:58:00Z", + "content_policy_version": "nightingale-patient-language.v0-candidate" + }, + "freshness": { + "status": "current", + "observed_at": "2026-07-26T14:55:00Z", + "patient_notice": null + }, + "uncertainty": { + "level": "medium", + "explanation": "This information may change as your care team learns more.", + "can_change": true + }, + "language": { + "locale": "en-US", + "release_state": "approved-source-language", + "plain_language_review": "approved" + }, + "correction": { + "state": "original", + "patient_notice": null + }, + "offline": { + "availability": "online-only", + "durable_storage_permitted": false + } + } + }, + "preparation": { + "value": "Please ask your nurse whether you need to prepare.", + "context": { + "release": { + "state": "released", + "released_at": "2026-07-26T14:58:00Z", + "content_policy_version": "nightingale-patient-language.v0-candidate" + }, + "freshness": { + "status": "current", + "observed_at": "2026-07-26T14:55:00Z", + "patient_notice": null + }, + "uncertainty": { + "level": "medium", + "explanation": "This information may change as your care team learns more.", + "can_change": true + }, + "language": { + "locale": "en-US", + "release_state": "approved-source-language", + "plain_language_review": "approved" + }, + "correction": { + "state": "original", + "patient_notice": null + }, + "offline": { + "availability": "online-only", + "durable_storage_permitted": false + } + } + }, + "can_change": { + "value": true, + "context": { + "release": { + "state": "released", + "released_at": "2026-07-26T14:58:00Z", + "content_policy_version": "nightingale-patient-language.v0-candidate" + }, + "freshness": { + "status": "current", + "observed_at": "2026-07-26T14:55:00Z", + "patient_notice": null + }, + "uncertainty": { + "level": "medium", + "explanation": "This information may change as your care team learns more.", + "can_change": true + }, + "language": { + "locale": "en-US", + "release_state": "approved-source-language", + "plain_language_review": "approved" + }, + "correction": { + "state": "original", + "patient_notice": null + }, + "offline": { + "availability": "online-only", + "durable_storage_permitted": false + } + } + } + } + ], + "patient_notice": null + }, + "next_steps": { + "state": "released", + "items": [ + { + "value": "Talk with your care team during rounds.", + "context": { + "release": { + "state": "released", + "released_at": "2026-07-26T14:58:00Z", + "content_policy_version": "nightingale-patient-language.v0-candidate" + }, + "freshness": { + "status": "current", + "observed_at": "2026-07-26T14:55:00Z", + "patient_notice": null + }, + "uncertainty": { + "level": "low", + "explanation": "This is the latest released information available for this item.", + "can_change": true + }, + "language": { + "locale": "en-US", + "release_state": "approved-source-language", + "plain_language_review": "approved" + }, + "correction": { + "state": "original", + "patient_notice": null + }, + "offline": { + "availability": "online-only", + "durable_storage_permitted": false + } + } + } + ], + "patient_notice": null + }, + "care_location": { + "state": "not-available", + "patient_notice": "This part of today’s plan is not available here. Ask your care team for the current information." + }, + "discharge_outlook": { + "state": "not-available", + "patient_notice": "This part of today’s plan is not available here. Ask your care team for the current information." + }, + "questions": { + "state": "released", + "items": [ + { + "value": "What should I expect before my next planned step?", + "context": { + "release": { + "state": "released", + "released_at": "2026-07-26T14:58:00Z", + "content_policy_version": "nightingale-patient-language.v0-candidate" + }, + "freshness": { + "status": "current", + "observed_at": "2026-07-26T14:55:00Z", + "patient_notice": null + }, + "uncertainty": { + "level": "low", + "explanation": "This is the latest released information available for this item.", + "can_change": true + }, + "language": { + "locale": "en-US", + "release_state": "approved-source-language", + "plain_language_review": "approved" + }, + "correction": { + "state": "original", + "patient_notice": null + }, + "offline": { + "availability": "online-only", + "durable_storage_permitted": false + } + } + } + ], + "patient_notice": null + }, + "notices": { + "state": "released", + "items": [ + { + "value": "Care plans can change. Ask your care team if this does not match what you were told.", + "context": { + "release": { + "state": "released", + "released_at": "2026-07-26T14:58:00Z", + "content_policy_version": "nightingale-patient-language.v0-candidate" + }, + "freshness": { + "status": "current", + "observed_at": "2026-07-26T14:55:00Z", + "patient_notice": null + }, + "uncertainty": { + "level": "low", + "explanation": "This is the latest released information available for this item.", + "can_change": true + }, + "language": { + "locale": "en-US", + "release_state": "approved-source-language", + "plain_language_review": "approved" + }, + "correction": { + "state": "original", + "patient_notice": null + }, + "offline": { + "availability": "online-only", + "durable_storage_permitted": false + } + } + } + ], + "patient_notice": null + } + }, + "revision_notice": null + }, + "meta": { + "request_id": "ntg_req_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "generated_at": "2026-07-26T15:00:00Z", + "policy_version": "nightingale-today-policy.v0-candidate", + "authorization_evaluated_at": "2026-07-26T15:00:00Z", + "inpatient_context_evaluated_at": "2026-07-26T15:00:00Z", + "locale": "en-US", + "completeness": "governed-evaluation-complete" + }, + "links": {} + } + }, + "success_approved_stale": { + "status": 200, + "headers": { + "Cache-Control": "private, no-store, max-age=0", + "Pragma": "no-cache", + "X-Robots-Tag": "noindex, nofollow, noarchive", + "Vary": "Authorization, Accept-Language", + "Content-Type": "application/json" + }, + "body": { + "data": { + "kind": "today", + "content_revision": "ntg_tdyrev_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "sections": { + "headline": { + "state": "released", + "field": { + "value": "Your care plan for today", + "context": { + "release": { + "state": "released", + "released_at": "2026-07-26T14:58:00Z", + "content_policy_version": "nightingale-patient-language.v0-candidate" + }, + "freshness": { + "status": "current", + "observed_at": "2026-07-26T14:55:00Z", + "patient_notice": null + }, + "uncertainty": { + "level": "low", + "explanation": "This is the latest released information available for this item.", + "can_change": true + }, + "language": { + "locale": "en-US", + "release_state": "approved-source-language", + "plain_language_review": "approved" + }, + "correction": { + "state": "original", + "patient_notice": null + }, + "offline": { + "availability": "online-only", + "durable_storage_permitted": false + } + } + } + }, + "summary": { + "state": "released", + "field": { + "value": "Your care team has released the following information for today.", + "context": { + "release": { + "state": "released", + "released_at": "2026-07-26T14:58:00Z", + "content_policy_version": "nightingale-patient-language.v0-candidate" + }, + "freshness": { + "status": "current", + "observed_at": "2026-07-26T14:55:00Z", + "patient_notice": null + }, + "uncertainty": { + "level": "low", + "explanation": "This is the latest released information available for this item.", + "can_change": true + }, + "language": { + "locale": "en-US", + "release_state": "approved-source-language", + "plain_language_review": "approved" + }, + "correction": { + "state": "original", + "patient_notice": null + }, + "offline": { + "availability": "online-only", + "durable_storage_permitted": false + } + } + } + }, + "schedule": { + "state": "released", + "items": [ + { + "item_handle": "ntg_tdyitem_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "label": { + "value": "Morning test", + "context": { + "release": { + "state": "released", + "released_at": "2026-07-26T14:58:00Z", + "content_policy_version": "nightingale-patient-language.v0-candidate" + }, + "freshness": { + "status": "current", + "observed_at": "2026-07-26T14:55:00Z", + "patient_notice": null + }, + "uncertainty": { + "level": "low", + "explanation": "This is the latest released information available for this item.", + "can_change": true + }, + "language": { + "locale": "en-US", + "release_state": "approved-source-language", + "plain_language_review": "approved" + }, + "correction": { + "state": "original", + "patient_notice": null + }, + "offline": { + "availability": "online-only", + "durable_storage_permitted": false + } + } + }, + "detail": { + "value": "Your care team will explain what to expect before the test.", + "context": { + "release": { + "state": "released", + "released_at": "2026-07-26T14:58:00Z", + "content_policy_version": "nightingale-patient-language.v0-candidate" + }, + "freshness": { + "status": "current", + "observed_at": "2026-07-26T14:55:00Z", + "patient_notice": null + }, + "uncertainty": { + "level": "low", + "explanation": "This is the latest released information available for this item.", + "can_change": true + }, + "language": { + "locale": "en-US", + "release_state": "approved-source-language", + "plain_language_review": "approved" + }, + "correction": { + "state": "original", + "patient_notice": null + }, + "offline": { + "availability": "online-only", + "durable_storage_permitted": false + } + } + }, + "status": { + "value": "planned", + "context": { + "release": { + "state": "released", + "released_at": "2026-07-26T14:58:00Z", + "content_policy_version": "nightingale-patient-language.v0-candidate" + }, + "freshness": { + "status": "current", + "observed_at": "2026-07-26T14:55:00Z", + "patient_notice": null + }, + "uncertainty": { + "level": "low", + "explanation": "This is the latest released information available for this item.", + "can_change": true + }, + "language": { + "locale": "en-US", + "release_state": "approved-source-language", + "plain_language_review": "approved" + }, + "correction": { + "state": "original", + "patient_notice": null + }, + "offline": { + "availability": "online-only", + "durable_storage_permitted": false + } + } + }, + "time_window": { + "value": "This morning", + "context": { + "release": { + "state": "released", + "released_at": "2026-07-26T12:05:00Z", + "content_policy_version": "nightingale-patient-language.v0-candidate" + }, + "freshness": { + "status": "approved-stale", + "observed_at": "2026-07-26T12:00:00Z", + "patient_notice": "This time window has not been refreshed recently and may have changed. Ask your care team before relying on it." + }, + "uncertainty": { + "level": "high", + "explanation": "This information may change as your care team learns more.", + "can_change": true + }, + "language": { + "locale": "en-US", + "release_state": "approved-source-language", + "plain_language_review": "approved" + }, + "correction": { + "state": "original", + "patient_notice": null + }, + "offline": { + "availability": "online-only", + "durable_storage_permitted": false + } + } + }, + "timing_confidence": { + "value": "estimated", + "context": { + "release": { + "state": "released", + "released_at": "2026-07-26T14:58:00Z", + "content_policy_version": "nightingale-patient-language.v0-candidate" + }, + "freshness": { + "status": "current", + "observed_at": "2026-07-26T14:55:00Z", + "patient_notice": null + }, + "uncertainty": { + "level": "medium", + "explanation": "This information may change as your care team learns more.", + "can_change": true + }, + "language": { + "locale": "en-US", + "release_state": "approved-source-language", + "plain_language_review": "approved" + }, + "correction": { + "state": "original", + "patient_notice": null + }, + "offline": { + "availability": "online-only", + "durable_storage_permitted": false + } + } + }, + "preparation": { + "value": "Please ask your nurse whether you need to prepare.", + "context": { + "release": { + "state": "released", + "released_at": "2026-07-26T14:58:00Z", + "content_policy_version": "nightingale-patient-language.v0-candidate" + }, + "freshness": { + "status": "current", + "observed_at": "2026-07-26T14:55:00Z", + "patient_notice": null + }, + "uncertainty": { + "level": "medium", + "explanation": "This information may change as your care team learns more.", + "can_change": true + }, + "language": { + "locale": "en-US", + "release_state": "approved-source-language", + "plain_language_review": "approved" + }, + "correction": { + "state": "original", + "patient_notice": null + }, + "offline": { + "availability": "online-only", + "durable_storage_permitted": false + } + } + }, + "can_change": { + "value": true, + "context": { + "release": { + "state": "released", + "released_at": "2026-07-26T14:58:00Z", + "content_policy_version": "nightingale-patient-language.v0-candidate" + }, + "freshness": { + "status": "current", + "observed_at": "2026-07-26T14:55:00Z", + "patient_notice": null + }, + "uncertainty": { + "level": "medium", + "explanation": "This information may change as your care team learns more.", + "can_change": true + }, + "language": { + "locale": "en-US", + "release_state": "approved-source-language", + "plain_language_review": "approved" + }, + "correction": { + "state": "original", + "patient_notice": null + }, + "offline": { + "availability": "online-only", + "durable_storage_permitted": false + } + } + } + } + ], + "patient_notice": null + }, + "next_steps": { + "state": "released", + "items": [ + { + "value": "Talk with your care team during rounds.", + "context": { + "release": { + "state": "released", + "released_at": "2026-07-26T14:58:00Z", + "content_policy_version": "nightingale-patient-language.v0-candidate" + }, + "freshness": { + "status": "current", + "observed_at": "2026-07-26T14:55:00Z", + "patient_notice": null + }, + "uncertainty": { + "level": "low", + "explanation": "This is the latest released information available for this item.", + "can_change": true + }, + "language": { + "locale": "en-US", + "release_state": "approved-source-language", + "plain_language_review": "approved" + }, + "correction": { + "state": "original", + "patient_notice": null + }, + "offline": { + "availability": "online-only", + "durable_storage_permitted": false + } + } + } + ], + "patient_notice": null + }, + "care_location": { + "state": "released", + "fields": { + "facility_display_name": { + "value": "Acumenus Medical Center", + "context": { + "release": { + "state": "released", + "released_at": "2026-07-26T14:58:00Z", + "content_policy_version": "nightingale-patient-language.v0-candidate" + }, + "freshness": { + "status": "current", + "observed_at": "2026-07-26T14:55:00Z", + "patient_notice": null + }, + "uncertainty": { + "level": "low", + "explanation": "This is the latest released information available for this item.", + "can_change": true + }, + "language": { + "locale": "en-US", + "release_state": "approved-source-language", + "plain_language_review": "approved" + }, + "correction": { + "state": "original", + "patient_notice": null + }, + "offline": { + "availability": "online-only", + "durable_storage_permitted": false + } + } + }, + "unit_display_name": { + "value": "Medical unit", + "context": { + "release": { + "state": "released", + "released_at": "2026-07-26T14:58:00Z", + "content_policy_version": "nightingale-patient-language.v0-candidate" + }, + "freshness": { + "status": "current", + "observed_at": "2026-07-26T14:55:00Z", + "patient_notice": null + }, + "uncertainty": { + "level": "low", + "explanation": "This is the latest released information available for this item.", + "can_change": true + }, + "language": { + "locale": "en-US", + "release_state": "approved-source-language", + "plain_language_review": "approved" + }, + "correction": { + "state": "original", + "patient_notice": null + }, + "offline": { + "availability": "online-only", + "durable_storage_permitted": false + } + } + }, + "room_display_name": { + "value": "Your current room", + "context": { + "release": { + "state": "released", + "released_at": "2026-07-26T14:58:00Z", + "content_policy_version": "nightingale-patient-language.v0-candidate" + }, + "freshness": { + "status": "current", + "observed_at": "2026-07-26T14:55:00Z", + "patient_notice": null + }, + "uncertainty": { + "level": "low", + "explanation": "This is the latest released information available for this item.", + "can_change": true + }, + "language": { + "locale": "en-US", + "release_state": "approved-source-language", + "plain_language_review": "approved" + }, + "correction": { + "state": "original", + "patient_notice": null + }, + "offline": { + "availability": "online-only", + "durable_storage_permitted": false + } + } + }, + "status": { + "value": "current", + "context": { + "release": { + "state": "released", + "released_at": "2026-07-26T14:58:00Z", + "content_policy_version": "nightingale-patient-language.v0-candidate" + }, + "freshness": { + "status": "current", + "observed_at": "2026-07-26T14:55:00Z", + "patient_notice": null + }, + "uncertainty": { + "level": "low", + "explanation": "This is the latest released information available for this item.", + "can_change": true + }, + "language": { + "locale": "en-US", + "release_state": "approved-source-language", + "plain_language_review": "approved" + }, + "correction": { + "state": "original", + "patient_notice": null + }, + "offline": { + "availability": "online-only", + "durable_storage_permitted": false + } + } + } + }, + "patient_notice": null + }, + "discharge_outlook": { + "state": "released", + "fields": { + "estimated_range": { + "value": "Your care team is still reviewing timing.", + "context": { + "release": { + "state": "released", + "released_at": "2026-07-26T14:58:00Z", + "content_policy_version": "nightingale-patient-language.v0-candidate" + }, + "freshness": { + "status": "current", + "observed_at": "2026-07-26T14:55:00Z", + "patient_notice": null + }, + "uncertainty": { + "level": "unknown", + "explanation": "This information may change as your care team learns more.", + "can_change": true + }, + "language": { + "locale": "en-US", + "release_state": "approved-source-language", + "plain_language_review": "approved" + }, + "correction": { + "state": "original", + "patient_notice": null + }, + "offline": { + "availability": "online-only", + "durable_storage_permitted": false + } + } + }, + "confidence": { + "value": "unknown", + "context": { + "release": { + "state": "released", + "released_at": "2026-07-26T14:58:00Z", + "content_policy_version": "nightingale-patient-language.v0-candidate" + }, + "freshness": { + "status": "current", + "observed_at": "2026-07-26T14:55:00Z", + "patient_notice": null + }, + "uncertainty": { + "level": "unknown", + "explanation": "This information may change as your care team learns more.", + "can_change": true + }, + "language": { + "locale": "en-US", + "release_state": "approved-source-language", + "plain_language_review": "approved" + }, + "correction": { + "state": "original", + "patient_notice": null + }, + "offline": { + "availability": "online-only", + "durable_storage_permitted": false + } + } + }, + "readiness_topics": { + "state": "released", + "items": [ + { + "value": "Safe movement and support at home.", + "context": { + "release": { + "state": "released", + "released_at": "2026-07-26T14:58:00Z", + "content_policy_version": "nightingale-patient-language.v0-candidate" + }, + "freshness": { + "status": "current", + "observed_at": "2026-07-26T14:55:00Z", + "patient_notice": null + }, + "uncertainty": { + "level": "low", + "explanation": "This is the latest released information available for this item.", + "can_change": true + }, + "language": { + "locale": "en-US", + "release_state": "approved-source-language", + "plain_language_review": "approved" + }, + "correction": { + "state": "original", + "patient_notice": null + }, + "offline": { + "availability": "online-only", + "durable_storage_permitted": false + } + } + } + ], + "patient_notice": null + }, + "remaining_steps": { + "state": "released", + "items": [ + { + "value": "Your team will review what needs to happen next.", + "context": { + "release": { + "state": "released", + "released_at": "2026-07-26T14:58:00Z", + "content_policy_version": "nightingale-patient-language.v0-candidate" + }, + "freshness": { + "status": "current", + "observed_at": "2026-07-26T14:55:00Z", + "patient_notice": null + }, + "uncertainty": { + "level": "low", + "explanation": "This is the latest released information available for this item.", + "can_change": true + }, + "language": { + "locale": "en-US", + "release_state": "approved-source-language", + "plain_language_review": "approved" + }, + "correction": { + "state": "original", + "patient_notice": null + }, + "offline": { + "availability": "online-only", + "durable_storage_permitted": false + } + } + } + ], + "patient_notice": null + }, + "can_change": { + "value": true, + "context": { + "release": { + "state": "released", + "released_at": "2026-07-26T14:58:00Z", + "content_policy_version": "nightingale-patient-language.v0-candidate" + }, + "freshness": { + "status": "current", + "observed_at": "2026-07-26T14:55:00Z", + "patient_notice": null + }, + "uncertainty": { + "level": "unknown", + "explanation": "This information may change as your care team learns more.", + "can_change": true + }, + "language": { + "locale": "en-US", + "release_state": "approved-source-language", + "plain_language_review": "approved" + }, + "correction": { + "state": "original", + "patient_notice": null + }, + "offline": { + "availability": "online-only", + "durable_storage_permitted": false + } + } + } + }, + "patient_notice": "This is not a promised discharge date. Timing can change." + }, + "questions": { + "state": "released", + "items": [ + { + "value": "What should I expect before my next planned step?", + "context": { + "release": { + "state": "released", + "released_at": "2026-07-26T14:58:00Z", + "content_policy_version": "nightingale-patient-language.v0-candidate" + }, + "freshness": { + "status": "current", + "observed_at": "2026-07-26T14:55:00Z", + "patient_notice": null + }, + "uncertainty": { + "level": "low", + "explanation": "This is the latest released information available for this item.", + "can_change": true + }, + "language": { + "locale": "en-US", + "release_state": "approved-source-language", + "plain_language_review": "approved" + }, + "correction": { + "state": "original", + "patient_notice": null + }, + "offline": { + "availability": "online-only", + "durable_storage_permitted": false + } + } + } + ], + "patient_notice": null + }, + "notices": { + "state": "released", + "items": [ + { + "value": "Care plans can change. Ask your care team if this does not match what you were told.", + "context": { + "release": { + "state": "released", + "released_at": "2026-07-26T14:58:00Z", + "content_policy_version": "nightingale-patient-language.v0-candidate" + }, + "freshness": { + "status": "current", + "observed_at": "2026-07-26T14:55:00Z", + "patient_notice": null + }, + "uncertainty": { + "level": "low", + "explanation": "This is the latest released information available for this item.", + "can_change": true + }, + "language": { + "locale": "en-US", + "release_state": "approved-source-language", + "plain_language_review": "approved" + }, + "correction": { + "state": "original", + "patient_notice": null + }, + "offline": { + "availability": "online-only", + "durable_storage_permitted": false + } + } + } + ], + "patient_notice": null + } + }, + "revision_notice": null + }, + "meta": { + "request_id": "ntg_req_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "generated_at": "2026-07-26T15:00:00Z", + "policy_version": "nightingale-today-policy.v0-candidate", + "authorization_evaluated_at": "2026-07-26T15:00:00Z", + "inpatient_context_evaluated_at": "2026-07-26T15:00:00Z", + "locale": "en-US", + "completeness": "governed-evaluation-complete" + }, + "links": {} + } + }, + "success_correction": { + "status": 200, + "headers": { + "Cache-Control": "private, no-store, max-age=0", + "Pragma": "no-cache", + "X-Robots-Tag": "noindex, nofollow, noarchive", + "Vary": "Authorization, Accept-Language", + "Content-Type": "application/json" + }, + "body": { + "data": { + "kind": "today", + "content_revision": "ntg_tdyrev_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "sections": { + "headline": { + "state": "released", + "field": { + "value": "Your care plan for today", + "context": { + "release": { + "state": "released", + "released_at": "2026-07-26T14:58:00Z", + "content_policy_version": "nightingale-patient-language.v0-candidate" + }, + "freshness": { + "status": "current", + "observed_at": "2026-07-26T14:55:00Z", + "patient_notice": null + }, + "uncertainty": { + "level": "low", + "explanation": "This is the latest released information available for this item.", + "can_change": true + }, + "language": { + "locale": "en-US", + "release_state": "approved-source-language", + "plain_language_review": "approved" + }, + "correction": { + "state": "original", + "patient_notice": null + }, + "offline": { + "availability": "online-only", + "durable_storage_permitted": false + } + } + } + }, + "summary": { + "state": "released", + "field": { + "value": "Your care team has released the following information for today.", + "context": { + "release": { + "state": "released", + "released_at": "2026-07-26T14:58:00Z", + "content_policy_version": "nightingale-patient-language.v0-candidate" + }, + "freshness": { + "status": "current", + "observed_at": "2026-07-26T14:55:00Z", + "patient_notice": null + }, + "uncertainty": { + "level": "low", + "explanation": "This is the latest released information available for this item.", + "can_change": true + }, + "language": { + "locale": "en-US", + "release_state": "approved-source-language", + "plain_language_review": "approved" + }, + "correction": { + "state": "original", + "patient_notice": null + }, + "offline": { + "availability": "online-only", + "durable_storage_permitted": false + } + } + } + }, + "schedule": { + "state": "released", + "items": [ + { + "item_handle": "ntg_tdyitem_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "label": { + "value": "Morning test", + "context": { + "release": { + "state": "released", + "released_at": "2026-07-26T14:58:00Z", + "content_policy_version": "nightingale-patient-language.v0-candidate" + }, + "freshness": { + "status": "current", + "observed_at": "2026-07-26T14:55:00Z", + "patient_notice": null + }, + "uncertainty": { + "level": "low", + "explanation": "This is the latest released information available for this item.", + "can_change": true + }, + "language": { + "locale": "en-US", + "release_state": "approved-source-language", + "plain_language_review": "approved" + }, + "correction": { + "state": "original", + "patient_notice": null + }, + "offline": { + "availability": "online-only", + "durable_storage_permitted": false + } + } + }, + "detail": { + "value": "Your care team will explain what to expect before the test.", + "context": { + "release": { + "state": "released", + "released_at": "2026-07-26T14:58:00Z", + "content_policy_version": "nightingale-patient-language.v0-candidate" + }, + "freshness": { + "status": "current", + "observed_at": "2026-07-26T14:55:00Z", + "patient_notice": null + }, + "uncertainty": { + "level": "low", + "explanation": "This is the latest released information available for this item.", + "can_change": true + }, + "language": { + "locale": "en-US", + "release_state": "approved-source-language", + "plain_language_review": "approved" + }, + "correction": { + "state": "original", + "patient_notice": null + }, + "offline": { + "availability": "online-only", + "durable_storage_permitted": false + } + } + }, + "status": { + "value": "planned", + "context": { + "release": { + "state": "released", + "released_at": "2026-07-26T14:58:00Z", + "content_policy_version": "nightingale-patient-language.v0-candidate" + }, + "freshness": { + "status": "current", + "observed_at": "2026-07-26T14:55:00Z", + "patient_notice": null + }, + "uncertainty": { + "level": "low", + "explanation": "This is the latest released information available for this item.", + "can_change": true + }, + "language": { + "locale": "en-US", + "release_state": "approved-source-language", + "plain_language_review": "approved" + }, + "correction": { + "state": "original", + "patient_notice": null + }, + "offline": { + "availability": "online-only", + "durable_storage_permitted": false + } + } + }, + "time_window": { + "value": "Later today", + "context": { + "release": { + "state": "released", + "released_at": "2026-07-26T14:58:00Z", + "content_policy_version": "nightingale-patient-language.v0-candidate" + }, + "freshness": { + "status": "current", + "observed_at": "2026-07-26T14:55:00Z", + "patient_notice": null + }, + "uncertainty": { + "level": "medium", + "explanation": "This information may change as your care team learns more.", + "can_change": true + }, + "language": { + "locale": "en-US", + "release_state": "approved-source-language", + "plain_language_review": "approved" + }, + "correction": { + "state": "corrected", + "patient_notice": "Your care team updated this information. Please use the details shown here." + }, + "offline": { + "availability": "online-only", + "durable_storage_permitted": false + } + } + }, + "timing_confidence": { + "value": "estimated", + "context": { + "release": { + "state": "released", + "released_at": "2026-07-26T14:58:00Z", + "content_policy_version": "nightingale-patient-language.v0-candidate" + }, + "freshness": { + "status": "current", + "observed_at": "2026-07-26T14:55:00Z", + "patient_notice": null + }, + "uncertainty": { + "level": "medium", + "explanation": "This information may change as your care team learns more.", + "can_change": true + }, + "language": { + "locale": "en-US", + "release_state": "approved-source-language", + "plain_language_review": "approved" + }, + "correction": { + "state": "original", + "patient_notice": null + }, + "offline": { + "availability": "online-only", + "durable_storage_permitted": false + } + } + }, + "preparation": { + "value": "Please ask your nurse whether you need to prepare.", + "context": { + "release": { + "state": "released", + "released_at": "2026-07-26T14:58:00Z", + "content_policy_version": "nightingale-patient-language.v0-candidate" + }, + "freshness": { + "status": "current", + "observed_at": "2026-07-26T14:55:00Z", + "patient_notice": null + }, + "uncertainty": { + "level": "medium", + "explanation": "This information may change as your care team learns more.", + "can_change": true + }, + "language": { + "locale": "en-US", + "release_state": "approved-source-language", + "plain_language_review": "approved" + }, + "correction": { + "state": "original", + "patient_notice": null + }, + "offline": { + "availability": "online-only", + "durable_storage_permitted": false + } + } + }, + "can_change": { + "value": true, + "context": { + "release": { + "state": "released", + "released_at": "2026-07-26T14:58:00Z", + "content_policy_version": "nightingale-patient-language.v0-candidate" + }, + "freshness": { + "status": "current", + "observed_at": "2026-07-26T14:55:00Z", + "patient_notice": null + }, + "uncertainty": { + "level": "medium", + "explanation": "This information may change as your care team learns more.", + "can_change": true + }, + "language": { + "locale": "en-US", + "release_state": "approved-source-language", + "plain_language_review": "approved" + }, + "correction": { + "state": "original", + "patient_notice": null + }, + "offline": { + "availability": "online-only", + "durable_storage_permitted": false + } + } + } + } + ], + "patient_notice": null + }, + "next_steps": { + "state": "released", + "items": [ + { + "value": "Talk with your care team during rounds.", + "context": { + "release": { + "state": "released", + "released_at": "2026-07-26T14:58:00Z", + "content_policy_version": "nightingale-patient-language.v0-candidate" + }, + "freshness": { + "status": "current", + "observed_at": "2026-07-26T14:55:00Z", + "patient_notice": null + }, + "uncertainty": { + "level": "low", + "explanation": "This is the latest released information available for this item.", + "can_change": true + }, + "language": { + "locale": "en-US", + "release_state": "approved-source-language", + "plain_language_review": "approved" + }, + "correction": { + "state": "original", + "patient_notice": null + }, + "offline": { + "availability": "online-only", + "durable_storage_permitted": false + } + } + } + ], + "patient_notice": null + }, + "care_location": { + "state": "released", + "fields": { + "facility_display_name": { + "value": "Acumenus Medical Center", + "context": { + "release": { + "state": "released", + "released_at": "2026-07-26T14:58:00Z", + "content_policy_version": "nightingale-patient-language.v0-candidate" + }, + "freshness": { + "status": "current", + "observed_at": "2026-07-26T14:55:00Z", + "patient_notice": null + }, + "uncertainty": { + "level": "low", + "explanation": "This is the latest released information available for this item.", + "can_change": true + }, + "language": { + "locale": "en-US", + "release_state": "approved-source-language", + "plain_language_review": "approved" + }, + "correction": { + "state": "original", + "patient_notice": null + }, + "offline": { + "availability": "online-only", + "durable_storage_permitted": false + } + } + }, + "unit_display_name": { + "value": "Medical unit", + "context": { + "release": { + "state": "released", + "released_at": "2026-07-26T14:58:00Z", + "content_policy_version": "nightingale-patient-language.v0-candidate" + }, + "freshness": { + "status": "current", + "observed_at": "2026-07-26T14:55:00Z", + "patient_notice": null + }, + "uncertainty": { + "level": "low", + "explanation": "This is the latest released information available for this item.", + "can_change": true + }, + "language": { + "locale": "en-US", + "release_state": "approved-source-language", + "plain_language_review": "approved" + }, + "correction": { + "state": "original", + "patient_notice": null + }, + "offline": { + "availability": "online-only", + "durable_storage_permitted": false + } + } + }, + "room_display_name": { + "value": "Your current room", + "context": { + "release": { + "state": "released", + "released_at": "2026-07-26T14:58:00Z", + "content_policy_version": "nightingale-patient-language.v0-candidate" + }, + "freshness": { + "status": "current", + "observed_at": "2026-07-26T14:55:00Z", + "patient_notice": null + }, + "uncertainty": { + "level": "low", + "explanation": "This is the latest released information available for this item.", + "can_change": true + }, + "language": { + "locale": "en-US", + "release_state": "approved-source-language", + "plain_language_review": "approved" + }, + "correction": { + "state": "original", + "patient_notice": null + }, + "offline": { + "availability": "online-only", + "durable_storage_permitted": false + } + } + }, + "status": { + "value": "current", + "context": { + "release": { + "state": "released", + "released_at": "2026-07-26T14:58:00Z", + "content_policy_version": "nightingale-patient-language.v0-candidate" + }, + "freshness": { + "status": "current", + "observed_at": "2026-07-26T14:55:00Z", + "patient_notice": null + }, + "uncertainty": { + "level": "low", + "explanation": "This is the latest released information available for this item.", + "can_change": true + }, + "language": { + "locale": "en-US", + "release_state": "approved-source-language", + "plain_language_review": "approved" + }, + "correction": { + "state": "original", + "patient_notice": null + }, + "offline": { + "availability": "online-only", + "durable_storage_permitted": false + } + } + } + }, + "patient_notice": null + }, + "discharge_outlook": { + "state": "released", + "fields": { + "estimated_range": { + "value": "Your care team is still reviewing timing.", + "context": { + "release": { + "state": "released", + "released_at": "2026-07-26T14:58:00Z", + "content_policy_version": "nightingale-patient-language.v0-candidate" + }, + "freshness": { + "status": "current", + "observed_at": "2026-07-26T14:55:00Z", + "patient_notice": null + }, + "uncertainty": { + "level": "unknown", + "explanation": "This information may change as your care team learns more.", + "can_change": true + }, + "language": { + "locale": "en-US", + "release_state": "approved-source-language", + "plain_language_review": "approved" + }, + "correction": { + "state": "original", + "patient_notice": null + }, + "offline": { + "availability": "online-only", + "durable_storage_permitted": false + } + } + }, + "confidence": { + "value": "unknown", + "context": { + "release": { + "state": "released", + "released_at": "2026-07-26T14:58:00Z", + "content_policy_version": "nightingale-patient-language.v0-candidate" + }, + "freshness": { + "status": "current", + "observed_at": "2026-07-26T14:55:00Z", + "patient_notice": null + }, + "uncertainty": { + "level": "unknown", + "explanation": "This information may change as your care team learns more.", + "can_change": true + }, + "language": { + "locale": "en-US", + "release_state": "approved-source-language", + "plain_language_review": "approved" + }, + "correction": { + "state": "original", + "patient_notice": null + }, + "offline": { + "availability": "online-only", + "durable_storage_permitted": false + } + } + }, + "readiness_topics": { + "state": "released", + "items": [ + { + "value": "Safe movement and support at home.", + "context": { + "release": { + "state": "released", + "released_at": "2026-07-26T14:58:00Z", + "content_policy_version": "nightingale-patient-language.v0-candidate" + }, + "freshness": { + "status": "current", + "observed_at": "2026-07-26T14:55:00Z", + "patient_notice": null + }, + "uncertainty": { + "level": "low", + "explanation": "This is the latest released information available for this item.", + "can_change": true + }, + "language": { + "locale": "en-US", + "release_state": "approved-source-language", + "plain_language_review": "approved" + }, + "correction": { + "state": "original", + "patient_notice": null + }, + "offline": { + "availability": "online-only", + "durable_storage_permitted": false + } + } + } + ], + "patient_notice": null + }, + "remaining_steps": { + "state": "released", + "items": [ + { + "value": "Your team will review what needs to happen next.", + "context": { + "release": { + "state": "released", + "released_at": "2026-07-26T14:58:00Z", + "content_policy_version": "nightingale-patient-language.v0-candidate" + }, + "freshness": { + "status": "current", + "observed_at": "2026-07-26T14:55:00Z", + "patient_notice": null + }, + "uncertainty": { + "level": "low", + "explanation": "This is the latest released information available for this item.", + "can_change": true + }, + "language": { + "locale": "en-US", + "release_state": "approved-source-language", + "plain_language_review": "approved" + }, + "correction": { + "state": "original", + "patient_notice": null + }, + "offline": { + "availability": "online-only", + "durable_storage_permitted": false + } + } + } + ], + "patient_notice": null + }, + "can_change": { + "value": true, + "context": { + "release": { + "state": "released", + "released_at": "2026-07-26T14:58:00Z", + "content_policy_version": "nightingale-patient-language.v0-candidate" + }, + "freshness": { + "status": "current", + "observed_at": "2026-07-26T14:55:00Z", + "patient_notice": null + }, + "uncertainty": { + "level": "unknown", + "explanation": "This information may change as your care team learns more.", + "can_change": true + }, + "language": { + "locale": "en-US", + "release_state": "approved-source-language", + "plain_language_review": "approved" + }, + "correction": { + "state": "original", + "patient_notice": null + }, + "offline": { + "availability": "online-only", + "durable_storage_permitted": false + } + } + } + }, + "patient_notice": "This is not a promised discharge date. Timing can change." + }, + "questions": { + "state": "released", + "items": [ + { + "value": "What should I expect before my next planned step?", + "context": { + "release": { + "state": "released", + "released_at": "2026-07-26T14:58:00Z", + "content_policy_version": "nightingale-patient-language.v0-candidate" + }, + "freshness": { + "status": "current", + "observed_at": "2026-07-26T14:55:00Z", + "patient_notice": null + }, + "uncertainty": { + "level": "low", + "explanation": "This is the latest released information available for this item.", + "can_change": true + }, + "language": { + "locale": "en-US", + "release_state": "approved-source-language", + "plain_language_review": "approved" + }, + "correction": { + "state": "original", + "patient_notice": null + }, + "offline": { + "availability": "online-only", + "durable_storage_permitted": false + } + } + } + ], + "patient_notice": null + }, + "notices": { + "state": "released", + "items": [ + { + "value": "Care plans can change. Ask your care team if this does not match what you were told.", + "context": { + "release": { + "state": "released", + "released_at": "2026-07-26T14:58:00Z", + "content_policy_version": "nightingale-patient-language.v0-candidate" + }, + "freshness": { + "status": "current", + "observed_at": "2026-07-26T14:55:00Z", + "patient_notice": null + }, + "uncertainty": { + "level": "low", + "explanation": "This is the latest released information available for this item.", + "can_change": true + }, + "language": { + "locale": "en-US", + "release_state": "approved-source-language", + "plain_language_review": "approved" + }, + "correction": { + "state": "original", + "patient_notice": null + }, + "offline": { + "availability": "online-only", + "durable_storage_permitted": false + } + } + } + ], + "patient_notice": null + } + }, + "revision_notice": { + "kind": "correction", + "message": "Your care team updated part of today’s plan. Please use the information shown here." + } + }, + "meta": { + "request_id": "ntg_req_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "generated_at": "2026-07-26T15:00:00Z", + "policy_version": "nightingale-today-policy.v0-candidate", + "authorization_evaluated_at": "2026-07-26T15:00:00Z", + "inpatient_context_evaluated_at": "2026-07-26T15:00:00Z", + "locale": "en-US", + "completeness": "governed-evaluation-complete" + }, + "links": {} + } + }, + "success_approved_translation": { + "status": 200, + "headers": { + "Cache-Control": "private, no-store, max-age=0", + "Pragma": "no-cache", + "X-Robots-Tag": "noindex, nofollow, noarchive", + "Vary": "Authorization, Accept-Language", + "Content-Type": "application/json" + }, + "body": { + "data": { + "kind": "today", + "content_revision": "ntg_tdyrev_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "sections": { + "headline": { + "state": "released", + "field": { + "value": "Your care plan for today", + "context": { + "release": { + "state": "released", + "released_at": "2026-07-26T14:58:00Z", + "content_policy_version": "nightingale-patient-language.v0-candidate" + }, + "freshness": { + "status": "current", + "observed_at": "2026-07-26T14:55:00Z", + "patient_notice": null + }, + "uncertainty": { + "level": "low", + "explanation": "This is the latest released information available for this item.", + "can_change": true + }, + "language": { + "locale": "en-US", + "release_state": "approved-translation", + "plain_language_review": "approved" + }, + "correction": { + "state": "original", + "patient_notice": null + }, + "offline": { + "availability": "online-only", + "durable_storage_permitted": false + } + } + } + }, + "summary": { + "state": "released", + "field": { + "value": "Your care team has released the following information for today.", + "context": { + "release": { + "state": "released", + "released_at": "2026-07-26T14:58:00Z", + "content_policy_version": "nightingale-patient-language.v0-candidate" + }, + "freshness": { + "status": "current", + "observed_at": "2026-07-26T14:55:00Z", + "patient_notice": null + }, + "uncertainty": { + "level": "low", + "explanation": "This is the latest released information available for this item.", + "can_change": true + }, + "language": { + "locale": "en-US", + "release_state": "approved-translation", + "plain_language_review": "approved" + }, + "correction": { + "state": "original", + "patient_notice": null + }, + "offline": { + "availability": "online-only", + "durable_storage_permitted": false + } + } + } + }, + "schedule": { + "state": "released", + "items": [ + { + "item_handle": "ntg_tdyitem_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "label": { + "value": "Morning test", + "context": { + "release": { + "state": "released", + "released_at": "2026-07-26T14:58:00Z", + "content_policy_version": "nightingale-patient-language.v0-candidate" + }, + "freshness": { + "status": "current", + "observed_at": "2026-07-26T14:55:00Z", + "patient_notice": null + }, + "uncertainty": { + "level": "low", + "explanation": "This is the latest released information available for this item.", + "can_change": true + }, + "language": { + "locale": "en-US", + "release_state": "approved-translation", + "plain_language_review": "approved" + }, + "correction": { + "state": "original", + "patient_notice": null + }, + "offline": { + "availability": "online-only", + "durable_storage_permitted": false + } + } + }, + "detail": { + "value": "Your care team will explain what to expect before the test.", + "context": { + "release": { + "state": "released", + "released_at": "2026-07-26T14:58:00Z", + "content_policy_version": "nightingale-patient-language.v0-candidate" + }, + "freshness": { + "status": "current", + "observed_at": "2026-07-26T14:55:00Z", + "patient_notice": null + }, + "uncertainty": { + "level": "low", + "explanation": "This is the latest released information available for this item.", + "can_change": true + }, + "language": { + "locale": "en-US", + "release_state": "approved-translation", + "plain_language_review": "approved" + }, + "correction": { + "state": "original", + "patient_notice": null + }, + "offline": { + "availability": "online-only", + "durable_storage_permitted": false + } + } + }, + "status": { + "value": "planned", + "context": { + "release": { + "state": "released", + "released_at": "2026-07-26T14:58:00Z", + "content_policy_version": "nightingale-patient-language.v0-candidate" + }, + "freshness": { + "status": "current", + "observed_at": "2026-07-26T14:55:00Z", + "patient_notice": null + }, + "uncertainty": { + "level": "low", + "explanation": "This is the latest released information available for this item.", + "can_change": true + }, + "language": { + "locale": "en-US", + "release_state": "approved-translation", + "plain_language_review": "approved" + }, + "correction": { + "state": "original", + "patient_notice": null + }, + "offline": { + "availability": "online-only", + "durable_storage_permitted": false + } + } + }, + "time_window": { + "value": "This morning", + "context": { + "release": { + "state": "released", + "released_at": "2026-07-26T14:58:00Z", + "content_policy_version": "nightingale-patient-language.v0-candidate" + }, + "freshness": { + "status": "current", + "observed_at": "2026-07-26T14:55:00Z", + "patient_notice": null + }, + "uncertainty": { + "level": "medium", + "explanation": "This information may change as your care team learns more.", + "can_change": true + }, + "language": { + "locale": "en-US", + "release_state": "approved-translation", + "plain_language_review": "approved" + }, + "correction": { + "state": "original", + "patient_notice": null + }, + "offline": { + "availability": "online-only", + "durable_storage_permitted": false + } + } + }, + "timing_confidence": { + "value": "estimated", + "context": { + "release": { + "state": "released", + "released_at": "2026-07-26T14:58:00Z", + "content_policy_version": "nightingale-patient-language.v0-candidate" + }, + "freshness": { + "status": "current", + "observed_at": "2026-07-26T14:55:00Z", + "patient_notice": null + }, + "uncertainty": { + "level": "medium", + "explanation": "This information may change as your care team learns more.", + "can_change": true + }, + "language": { + "locale": "en-US", + "release_state": "approved-translation", + "plain_language_review": "approved" + }, + "correction": { + "state": "original", + "patient_notice": null + }, + "offline": { + "availability": "online-only", + "durable_storage_permitted": false + } + } + }, + "preparation": { + "value": "Please ask your nurse whether you need to prepare.", + "context": { + "release": { + "state": "released", + "released_at": "2026-07-26T14:58:00Z", + "content_policy_version": "nightingale-patient-language.v0-candidate" + }, + "freshness": { + "status": "current", + "observed_at": "2026-07-26T14:55:00Z", + "patient_notice": null + }, + "uncertainty": { + "level": "medium", + "explanation": "This information may change as your care team learns more.", + "can_change": true + }, + "language": { + "locale": "en-US", + "release_state": "approved-translation", + "plain_language_review": "approved" + }, + "correction": { + "state": "original", + "patient_notice": null + }, + "offline": { + "availability": "online-only", + "durable_storage_permitted": false + } + } + }, + "can_change": { + "value": true, + "context": { + "release": { + "state": "released", + "released_at": "2026-07-26T14:58:00Z", + "content_policy_version": "nightingale-patient-language.v0-candidate" + }, + "freshness": { + "status": "current", + "observed_at": "2026-07-26T14:55:00Z", + "patient_notice": null + }, + "uncertainty": { + "level": "medium", + "explanation": "This information may change as your care team learns more.", + "can_change": true + }, + "language": { + "locale": "en-US", + "release_state": "approved-translation", + "plain_language_review": "approved" + }, + "correction": { + "state": "original", + "patient_notice": null + }, + "offline": { + "availability": "online-only", + "durable_storage_permitted": false + } + } + } + } + ], + "patient_notice": null + }, + "next_steps": { + "state": "released", + "items": [ + { + "value": "Talk with your care team during rounds.", + "context": { + "release": { + "state": "released", + "released_at": "2026-07-26T14:58:00Z", + "content_policy_version": "nightingale-patient-language.v0-candidate" + }, + "freshness": { + "status": "current", + "observed_at": "2026-07-26T14:55:00Z", + "patient_notice": null + }, + "uncertainty": { + "level": "low", + "explanation": "This is the latest released information available for this item.", + "can_change": true + }, + "language": { + "locale": "en-US", + "release_state": "approved-translation", + "plain_language_review": "approved" + }, + "correction": { + "state": "original", + "patient_notice": null + }, + "offline": { + "availability": "online-only", + "durable_storage_permitted": false + } + } + } + ], + "patient_notice": null + }, + "care_location": { + "state": "released", + "fields": { + "facility_display_name": { + "value": "Acumenus Medical Center", + "context": { + "release": { + "state": "released", + "released_at": "2026-07-26T14:58:00Z", + "content_policy_version": "nightingale-patient-language.v0-candidate" + }, + "freshness": { + "status": "current", + "observed_at": "2026-07-26T14:55:00Z", + "patient_notice": null + }, + "uncertainty": { + "level": "low", + "explanation": "This is the latest released information available for this item.", + "can_change": true + }, + "language": { + "locale": "en-US", + "release_state": "approved-translation", + "plain_language_review": "approved" + }, + "correction": { + "state": "original", + "patient_notice": null + }, + "offline": { + "availability": "online-only", + "durable_storage_permitted": false + } + } + }, + "unit_display_name": { + "value": "Medical unit", + "context": { + "release": { + "state": "released", + "released_at": "2026-07-26T14:58:00Z", + "content_policy_version": "nightingale-patient-language.v0-candidate" + }, + "freshness": { + "status": "current", + "observed_at": "2026-07-26T14:55:00Z", + "patient_notice": null + }, + "uncertainty": { + "level": "low", + "explanation": "This is the latest released information available for this item.", + "can_change": true + }, + "language": { + "locale": "en-US", + "release_state": "approved-translation", + "plain_language_review": "approved" + }, + "correction": { + "state": "original", + "patient_notice": null + }, + "offline": { + "availability": "online-only", + "durable_storage_permitted": false + } + } + }, + "room_display_name": { + "value": "Your current room", + "context": { + "release": { + "state": "released", + "released_at": "2026-07-26T14:58:00Z", + "content_policy_version": "nightingale-patient-language.v0-candidate" + }, + "freshness": { + "status": "current", + "observed_at": "2026-07-26T14:55:00Z", + "patient_notice": null + }, + "uncertainty": { + "level": "low", + "explanation": "This is the latest released information available for this item.", + "can_change": true + }, + "language": { + "locale": "en-US", + "release_state": "approved-translation", + "plain_language_review": "approved" + }, + "correction": { + "state": "original", + "patient_notice": null + }, + "offline": { + "availability": "online-only", + "durable_storage_permitted": false + } + } + }, + "status": { + "value": "current", + "context": { + "release": { + "state": "released", + "released_at": "2026-07-26T14:58:00Z", + "content_policy_version": "nightingale-patient-language.v0-candidate" + }, + "freshness": { + "status": "current", + "observed_at": "2026-07-26T14:55:00Z", + "patient_notice": null + }, + "uncertainty": { + "level": "low", + "explanation": "This is the latest released information available for this item.", + "can_change": true + }, + "language": { + "locale": "en-US", + "release_state": "approved-translation", + "plain_language_review": "approved" + }, + "correction": { + "state": "original", + "patient_notice": null + }, + "offline": { + "availability": "online-only", + "durable_storage_permitted": false + } + } + } + }, + "patient_notice": null + }, + "discharge_outlook": { + "state": "released", + "fields": { + "estimated_range": { + "value": "Your care team is still reviewing timing.", + "context": { + "release": { + "state": "released", + "released_at": "2026-07-26T14:58:00Z", + "content_policy_version": "nightingale-patient-language.v0-candidate" + }, + "freshness": { + "status": "current", + "observed_at": "2026-07-26T14:55:00Z", + "patient_notice": null + }, + "uncertainty": { + "level": "unknown", + "explanation": "This information may change as your care team learns more.", + "can_change": true + }, + "language": { + "locale": "en-US", + "release_state": "approved-translation", + "plain_language_review": "approved" + }, + "correction": { + "state": "original", + "patient_notice": null + }, + "offline": { + "availability": "online-only", + "durable_storage_permitted": false + } + } + }, + "confidence": { + "value": "unknown", + "context": { + "release": { + "state": "released", + "released_at": "2026-07-26T14:58:00Z", + "content_policy_version": "nightingale-patient-language.v0-candidate" + }, + "freshness": { + "status": "current", + "observed_at": "2026-07-26T14:55:00Z", + "patient_notice": null + }, + "uncertainty": { + "level": "unknown", + "explanation": "This information may change as your care team learns more.", + "can_change": true + }, + "language": { + "locale": "en-US", + "release_state": "approved-translation", + "plain_language_review": "approved" + }, + "correction": { + "state": "original", + "patient_notice": null + }, + "offline": { + "availability": "online-only", + "durable_storage_permitted": false + } + } + }, + "readiness_topics": { + "state": "released", + "items": [ + { + "value": "Safe movement and support at home.", + "context": { + "release": { + "state": "released", + "released_at": "2026-07-26T14:58:00Z", + "content_policy_version": "nightingale-patient-language.v0-candidate" + }, + "freshness": { + "status": "current", + "observed_at": "2026-07-26T14:55:00Z", + "patient_notice": null + }, + "uncertainty": { + "level": "low", + "explanation": "This is the latest released information available for this item.", + "can_change": true + }, + "language": { + "locale": "en-US", + "release_state": "approved-translation", + "plain_language_review": "approved" + }, + "correction": { + "state": "original", + "patient_notice": null + }, + "offline": { + "availability": "online-only", + "durable_storage_permitted": false + } + } + } + ], + "patient_notice": null + }, + "remaining_steps": { + "state": "released", + "items": [ + { + "value": "Your team will review what needs to happen next.", + "context": { + "release": { + "state": "released", + "released_at": "2026-07-26T14:58:00Z", + "content_policy_version": "nightingale-patient-language.v0-candidate" + }, + "freshness": { + "status": "current", + "observed_at": "2026-07-26T14:55:00Z", + "patient_notice": null + }, + "uncertainty": { + "level": "low", + "explanation": "This is the latest released information available for this item.", + "can_change": true + }, + "language": { + "locale": "en-US", + "release_state": "approved-translation", + "plain_language_review": "approved" + }, + "correction": { + "state": "original", + "patient_notice": null + }, + "offline": { + "availability": "online-only", + "durable_storage_permitted": false + } + } + } + ], + "patient_notice": null + }, + "can_change": { + "value": true, + "context": { + "release": { + "state": "released", + "released_at": "2026-07-26T14:58:00Z", + "content_policy_version": "nightingale-patient-language.v0-candidate" + }, + "freshness": { + "status": "current", + "observed_at": "2026-07-26T14:55:00Z", + "patient_notice": null + }, + "uncertainty": { + "level": "unknown", + "explanation": "This information may change as your care team learns more.", + "can_change": true + }, + "language": { + "locale": "en-US", + "release_state": "approved-translation", + "plain_language_review": "approved" + }, + "correction": { + "state": "original", + "patient_notice": null + }, + "offline": { + "availability": "online-only", + "durable_storage_permitted": false + } + } + } + }, + "patient_notice": "This is not a promised discharge date. Timing can change." + }, + "questions": { + "state": "released", + "items": [ + { + "value": "What should I expect before my next planned step?", + "context": { + "release": { + "state": "released", + "released_at": "2026-07-26T14:58:00Z", + "content_policy_version": "nightingale-patient-language.v0-candidate" + }, + "freshness": { + "status": "current", + "observed_at": "2026-07-26T14:55:00Z", + "patient_notice": null + }, + "uncertainty": { + "level": "low", + "explanation": "This is the latest released information available for this item.", + "can_change": true + }, + "language": { + "locale": "en-US", + "release_state": "approved-translation", + "plain_language_review": "approved" + }, + "correction": { + "state": "original", + "patient_notice": null + }, + "offline": { + "availability": "online-only", + "durable_storage_permitted": false + } + } + } + ], + "patient_notice": null + }, + "notices": { + "state": "released", + "items": [ + { + "value": "Care plans can change. Ask your care team if this does not match what you were told.", + "context": { + "release": { + "state": "released", + "released_at": "2026-07-26T14:58:00Z", + "content_policy_version": "nightingale-patient-language.v0-candidate" + }, + "freshness": { + "status": "current", + "observed_at": "2026-07-26T14:55:00Z", + "patient_notice": null + }, + "uncertainty": { + "level": "low", + "explanation": "This is the latest released information available for this item.", + "can_change": true + }, + "language": { + "locale": "en-US", + "release_state": "approved-translation", + "plain_language_review": "approved" + }, + "correction": { + "state": "original", + "patient_notice": null + }, + "offline": { + "availability": "online-only", + "durable_storage_permitted": false + } + } + } + ], + "patient_notice": null + } + }, + "revision_notice": null + }, + "meta": { + "request_id": "ntg_req_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "generated_at": "2026-07-26T15:00:00Z", + "policy_version": "nightingale-today-policy.v0-candidate", + "authorization_evaluated_at": "2026-07-26T15:00:00Z", + "inpatient_context_evaluated_at": "2026-07-26T15:00:00Z", + "locale": "en-US", + "completeness": "governed-evaluation-complete" + }, + "links": {} + } + }, + "success_approved_source_language": { + "status": 200, + "headers": { + "Cache-Control": "private, no-store, max-age=0", + "Pragma": "no-cache", + "X-Robots-Tag": "noindex, nofollow, noarchive", + "Vary": "Authorization, Accept-Language", + "Content-Type": "application/json" + }, + "body": { + "data": { + "kind": "today", + "content_revision": "ntg_tdyrev_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "sections": { + "headline": { + "state": "released", + "field": { + "value": "Your care plan for today", + "context": { + "release": { + "state": "released", + "released_at": "2026-07-26T14:58:00Z", + "content_policy_version": "nightingale-patient-language.v0-candidate" + }, + "freshness": { + "status": "current", + "observed_at": "2026-07-26T14:55:00Z", + "patient_notice": null + }, + "uncertainty": { + "level": "low", + "explanation": "This is the latest released information available for this item.", + "can_change": true + }, + "language": { + "locale": "en-US", + "release_state": "approved-source-language", + "plain_language_review": "approved" + }, + "correction": { + "state": "original", + "patient_notice": null + }, + "offline": { + "availability": "online-only", + "durable_storage_permitted": false + } + } + } + }, + "summary": { + "state": "released", + "field": { + "value": "Your care team has released the following information for today.", + "context": { + "release": { + "state": "released", + "released_at": "2026-07-26T14:58:00Z", + "content_policy_version": "nightingale-patient-language.v0-candidate" + }, + "freshness": { + "status": "current", + "observed_at": "2026-07-26T14:55:00Z", + "patient_notice": null + }, + "uncertainty": { + "level": "low", + "explanation": "This is the latest released information available for this item.", + "can_change": true + }, + "language": { + "locale": "en-US", + "release_state": "approved-source-language", + "plain_language_review": "approved" + }, + "correction": { + "state": "original", + "patient_notice": null + }, + "offline": { + "availability": "online-only", + "durable_storage_permitted": false + } + } + } + }, + "schedule": { + "state": "released", + "items": [ + { + "item_handle": "ntg_tdyitem_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "label": { + "value": "Morning test", + "context": { + "release": { + "state": "released", + "released_at": "2026-07-26T14:58:00Z", + "content_policy_version": "nightingale-patient-language.v0-candidate" + }, + "freshness": { + "status": "current", + "observed_at": "2026-07-26T14:55:00Z", + "patient_notice": null + }, + "uncertainty": { + "level": "low", + "explanation": "This is the latest released information available for this item.", + "can_change": true + }, + "language": { + "locale": "en-US", + "release_state": "approved-source-language", + "plain_language_review": "approved" + }, + "correction": { + "state": "original", + "patient_notice": null + }, + "offline": { + "availability": "online-only", + "durable_storage_permitted": false + } + } + }, + "detail": { + "value": "Your care team will explain what to expect before the test.", + "context": { + "release": { + "state": "released", + "released_at": "2026-07-26T14:58:00Z", + "content_policy_version": "nightingale-patient-language.v0-candidate" + }, + "freshness": { + "status": "current", + "observed_at": "2026-07-26T14:55:00Z", + "patient_notice": null + }, + "uncertainty": { + "level": "low", + "explanation": "This is the latest released information available for this item.", + "can_change": true + }, + "language": { + "locale": "en-US", + "release_state": "approved-source-language", + "plain_language_review": "approved" + }, + "correction": { + "state": "original", + "patient_notice": null + }, + "offline": { + "availability": "online-only", + "durable_storage_permitted": false + } + } + }, + "status": { + "value": "planned", + "context": { + "release": { + "state": "released", + "released_at": "2026-07-26T14:58:00Z", + "content_policy_version": "nightingale-patient-language.v0-candidate" + }, + "freshness": { + "status": "current", + "observed_at": "2026-07-26T14:55:00Z", + "patient_notice": null + }, + "uncertainty": { + "level": "low", + "explanation": "This is the latest released information available for this item.", + "can_change": true + }, + "language": { + "locale": "en-US", + "release_state": "approved-source-language", + "plain_language_review": "approved" + }, + "correction": { + "state": "original", + "patient_notice": null + }, + "offline": { + "availability": "online-only", + "durable_storage_permitted": false + } + } + }, + "time_window": { + "value": "This morning", + "context": { + "release": { + "state": "released", + "released_at": "2026-07-26T14:58:00Z", + "content_policy_version": "nightingale-patient-language.v0-candidate" + }, + "freshness": { + "status": "current", + "observed_at": "2026-07-26T14:55:00Z", + "patient_notice": null + }, + "uncertainty": { + "level": "medium", + "explanation": "This information may change as your care team learns more.", + "can_change": true + }, + "language": { + "locale": "en-US", + "release_state": "approved-source-language", + "plain_language_review": "approved" + }, + "correction": { + "state": "original", + "patient_notice": null + }, + "offline": { + "availability": "online-only", + "durable_storage_permitted": false + } + } + }, + "timing_confidence": { + "value": "estimated", + "context": { + "release": { + "state": "released", + "released_at": "2026-07-26T14:58:00Z", + "content_policy_version": "nightingale-patient-language.v0-candidate" + }, + "freshness": { + "status": "current", + "observed_at": "2026-07-26T14:55:00Z", + "patient_notice": null + }, + "uncertainty": { + "level": "medium", + "explanation": "This information may change as your care team learns more.", + "can_change": true + }, + "language": { + "locale": "en-US", + "release_state": "approved-source-language", + "plain_language_review": "approved" + }, + "correction": { + "state": "original", + "patient_notice": null + }, + "offline": { + "availability": "online-only", + "durable_storage_permitted": false + } + } + }, + "preparation": { + "value": "Please ask your nurse whether you need to prepare.", + "context": { + "release": { + "state": "released", + "released_at": "2026-07-26T14:58:00Z", + "content_policy_version": "nightingale-patient-language.v0-candidate" + }, + "freshness": { + "status": "current", + "observed_at": "2026-07-26T14:55:00Z", + "patient_notice": null + }, + "uncertainty": { + "level": "medium", + "explanation": "This information may change as your care team learns more.", + "can_change": true + }, + "language": { + "locale": "en-US", + "release_state": "approved-source-language", + "plain_language_review": "approved" + }, + "correction": { + "state": "original", + "patient_notice": null + }, + "offline": { + "availability": "online-only", + "durable_storage_permitted": false + } + } + }, + "can_change": { + "value": true, + "context": { + "release": { + "state": "released", + "released_at": "2026-07-26T14:58:00Z", + "content_policy_version": "nightingale-patient-language.v0-candidate" + }, + "freshness": { + "status": "current", + "observed_at": "2026-07-26T14:55:00Z", + "patient_notice": null + }, + "uncertainty": { + "level": "medium", + "explanation": "This information may change as your care team learns more.", + "can_change": true + }, + "language": { + "locale": "en-US", + "release_state": "approved-source-language", + "plain_language_review": "approved" + }, + "correction": { + "state": "original", + "patient_notice": null + }, + "offline": { + "availability": "online-only", + "durable_storage_permitted": false + } + } + } + } + ], + "patient_notice": null + }, + "next_steps": { + "state": "released", + "items": [ + { + "value": "Talk with your care team during rounds.", + "context": { + "release": { + "state": "released", + "released_at": "2026-07-26T14:58:00Z", + "content_policy_version": "nightingale-patient-language.v0-candidate" + }, + "freshness": { + "status": "current", + "observed_at": "2026-07-26T14:55:00Z", + "patient_notice": null + }, + "uncertainty": { + "level": "low", + "explanation": "This is the latest released information available for this item.", + "can_change": true + }, + "language": { + "locale": "en-US", + "release_state": "approved-source-language", + "plain_language_review": "approved" + }, + "correction": { + "state": "original", + "patient_notice": null + }, + "offline": { + "availability": "online-only", + "durable_storage_permitted": false + } + } + } + ], + "patient_notice": null + }, + "care_location": { + "state": "released", + "fields": { + "facility_display_name": { + "value": "Acumenus Medical Center", + "context": { + "release": { + "state": "released", + "released_at": "2026-07-26T14:58:00Z", + "content_policy_version": "nightingale-patient-language.v0-candidate" + }, + "freshness": { + "status": "current", + "observed_at": "2026-07-26T14:55:00Z", + "patient_notice": null + }, + "uncertainty": { + "level": "low", + "explanation": "This is the latest released information available for this item.", + "can_change": true + }, + "language": { + "locale": "en-US", + "release_state": "approved-source-language", + "plain_language_review": "approved" + }, + "correction": { + "state": "original", + "patient_notice": null + }, + "offline": { + "availability": "online-only", + "durable_storage_permitted": false + } + } + }, + "unit_display_name": { + "value": "Medical unit", + "context": { + "release": { + "state": "released", + "released_at": "2026-07-26T14:58:00Z", + "content_policy_version": "nightingale-patient-language.v0-candidate" + }, + "freshness": { + "status": "current", + "observed_at": "2026-07-26T14:55:00Z", + "patient_notice": null + }, + "uncertainty": { + "level": "low", + "explanation": "This is the latest released information available for this item.", + "can_change": true + }, + "language": { + "locale": "en-US", + "release_state": "approved-source-language", + "plain_language_review": "approved" + }, + "correction": { + "state": "original", + "patient_notice": null + }, + "offline": { + "availability": "online-only", + "durable_storage_permitted": false + } + } + }, + "room_display_name": { + "value": "Your current room", + "context": { + "release": { + "state": "released", + "released_at": "2026-07-26T14:58:00Z", + "content_policy_version": "nightingale-patient-language.v0-candidate" + }, + "freshness": { + "status": "current", + "observed_at": "2026-07-26T14:55:00Z", + "patient_notice": null + }, + "uncertainty": { + "level": "low", + "explanation": "This is the latest released information available for this item.", + "can_change": true + }, + "language": { + "locale": "en-US", + "release_state": "approved-source-language", + "plain_language_review": "approved" + }, + "correction": { + "state": "original", + "patient_notice": null + }, + "offline": { + "availability": "online-only", + "durable_storage_permitted": false + } + } + }, + "status": { + "value": "current", + "context": { + "release": { + "state": "released", + "released_at": "2026-07-26T14:58:00Z", + "content_policy_version": "nightingale-patient-language.v0-candidate" + }, + "freshness": { + "status": "current", + "observed_at": "2026-07-26T14:55:00Z", + "patient_notice": null + }, + "uncertainty": { + "level": "low", + "explanation": "This is the latest released information available for this item.", + "can_change": true + }, + "language": { + "locale": "en-US", + "release_state": "approved-source-language", + "plain_language_review": "approved" + }, + "correction": { + "state": "original", + "patient_notice": null + }, + "offline": { + "availability": "online-only", + "durable_storage_permitted": false + } + } + } + }, + "patient_notice": null + }, + "discharge_outlook": { + "state": "released", + "fields": { + "estimated_range": { + "value": "Your care team is still reviewing timing.", + "context": { + "release": { + "state": "released", + "released_at": "2026-07-26T14:58:00Z", + "content_policy_version": "nightingale-patient-language.v0-candidate" + }, + "freshness": { + "status": "current", + "observed_at": "2026-07-26T14:55:00Z", + "patient_notice": null + }, + "uncertainty": { + "level": "unknown", + "explanation": "This information may change as your care team learns more.", + "can_change": true + }, + "language": { + "locale": "en-US", + "release_state": "approved-source-language", + "plain_language_review": "approved" + }, + "correction": { + "state": "original", + "patient_notice": null + }, + "offline": { + "availability": "online-only", + "durable_storage_permitted": false + } + } + }, + "confidence": { + "value": "unknown", + "context": { + "release": { + "state": "released", + "released_at": "2026-07-26T14:58:00Z", + "content_policy_version": "nightingale-patient-language.v0-candidate" + }, + "freshness": { + "status": "current", + "observed_at": "2026-07-26T14:55:00Z", + "patient_notice": null + }, + "uncertainty": { + "level": "unknown", + "explanation": "This information may change as your care team learns more.", + "can_change": true + }, + "language": { + "locale": "en-US", + "release_state": "approved-source-language", + "plain_language_review": "approved" + }, + "correction": { + "state": "original", + "patient_notice": null + }, + "offline": { + "availability": "online-only", + "durable_storage_permitted": false + } + } + }, + "readiness_topics": { + "state": "released", + "items": [ + { + "value": "Safe movement and support at home.", + "context": { + "release": { + "state": "released", + "released_at": "2026-07-26T14:58:00Z", + "content_policy_version": "nightingale-patient-language.v0-candidate" + }, + "freshness": { + "status": "current", + "observed_at": "2026-07-26T14:55:00Z", + "patient_notice": null + }, + "uncertainty": { + "level": "low", + "explanation": "This is the latest released information available for this item.", + "can_change": true + }, + "language": { + "locale": "en-US", + "release_state": "approved-source-language", + "plain_language_review": "approved" + }, + "correction": { + "state": "original", + "patient_notice": null + }, + "offline": { + "availability": "online-only", + "durable_storage_permitted": false + } + } + } + ], + "patient_notice": null + }, + "remaining_steps": { + "state": "released", + "items": [ + { + "value": "Your team will review what needs to happen next.", + "context": { + "release": { + "state": "released", + "released_at": "2026-07-26T14:58:00Z", + "content_policy_version": "nightingale-patient-language.v0-candidate" + }, + "freshness": { + "status": "current", + "observed_at": "2026-07-26T14:55:00Z", + "patient_notice": null + }, + "uncertainty": { + "level": "low", + "explanation": "This is the latest released information available for this item.", + "can_change": true + }, + "language": { + "locale": "en-US", + "release_state": "approved-source-language", + "plain_language_review": "approved" + }, + "correction": { + "state": "original", + "patient_notice": null + }, + "offline": { + "availability": "online-only", + "durable_storage_permitted": false + } + } + } + ], + "patient_notice": null + }, + "can_change": { + "value": true, + "context": { + "release": { + "state": "released", + "released_at": "2026-07-26T14:58:00Z", + "content_policy_version": "nightingale-patient-language.v0-candidate" + }, + "freshness": { + "status": "current", + "observed_at": "2026-07-26T14:55:00Z", + "patient_notice": null + }, + "uncertainty": { + "level": "unknown", + "explanation": "This information may change as your care team learns more.", + "can_change": true + }, + "language": { + "locale": "en-US", + "release_state": "approved-source-language", + "plain_language_review": "approved" + }, + "correction": { + "state": "original", + "patient_notice": null + }, + "offline": { + "availability": "online-only", + "durable_storage_permitted": false + } + } + } + }, + "patient_notice": "This is not a promised discharge date. Timing can change." + }, + "questions": { + "state": "released", + "items": [ + { + "value": "What should I expect before my next planned step?", + "context": { + "release": { + "state": "released", + "released_at": "2026-07-26T14:58:00Z", + "content_policy_version": "nightingale-patient-language.v0-candidate" + }, + "freshness": { + "status": "current", + "observed_at": "2026-07-26T14:55:00Z", + "patient_notice": null + }, + "uncertainty": { + "level": "low", + "explanation": "This is the latest released information available for this item.", + "can_change": true + }, + "language": { + "locale": "en-US", + "release_state": "approved-source-language", + "plain_language_review": "approved" + }, + "correction": { + "state": "original", + "patient_notice": null + }, + "offline": { + "availability": "online-only", + "durable_storage_permitted": false + } + } + } + ], + "patient_notice": null + }, + "notices": { + "state": "released", + "items": [ + { + "value": "Care plans can change. Ask your care team if this does not match what you were told.", + "context": { + "release": { + "state": "released", + "released_at": "2026-07-26T14:58:00Z", + "content_policy_version": "nightingale-patient-language.v0-candidate" + }, + "freshness": { + "status": "current", + "observed_at": "2026-07-26T14:55:00Z", + "patient_notice": null + }, + "uncertainty": { + "level": "low", + "explanation": "This is the latest released information available for this item.", + "can_change": true + }, + "language": { + "locale": "en-US", + "release_state": "approved-source-language", + "plain_language_review": "approved" + }, + "correction": { + "state": "original", + "patient_notice": null + }, + "offline": { + "availability": "online-only", + "durable_storage_permitted": false + } + } + } + ], + "patient_notice": null + } + }, + "revision_notice": null + }, + "meta": { + "request_id": "ntg_req_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "generated_at": "2026-07-26T15:00:00Z", + "policy_version": "nightingale-today-policy.v0-candidate", + "authorization_evaluated_at": "2026-07-26T15:00:00Z", + "inpatient_context_evaluated_at": "2026-07-26T15:00:00Z", + "locale": "en-US", + "completeness": "governed-evaluation-complete" + }, + "links": {} + } + }, + "success_delayed_high_uncertainty": { + "status": 200, + "headers": { + "Cache-Control": "private, no-store, max-age=0", + "Pragma": "no-cache", + "X-Robots-Tag": "noindex, nofollow, noarchive", + "Vary": "Authorization, Accept-Language", + "Content-Type": "application/json" + }, + "body": { + "data": { + "kind": "today", + "content_revision": "ntg_tdyrev_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "sections": { + "headline": { + "state": "released", + "field": { + "value": "Your care plan for today", + "context": { + "release": { + "state": "released", + "released_at": "2026-07-26T14:58:00Z", + "content_policy_version": "nightingale-patient-language.v0-candidate" + }, + "freshness": { + "status": "current", + "observed_at": "2026-07-26T14:55:00Z", + "patient_notice": null + }, + "uncertainty": { + "level": "low", + "explanation": "This is the latest released information available for this item.", + "can_change": true + }, + "language": { + "locale": "en-US", + "release_state": "approved-source-language", + "plain_language_review": "approved" + }, + "correction": { + "state": "original", + "patient_notice": null + }, + "offline": { + "availability": "online-only", + "durable_storage_permitted": false + } + } + } + }, + "summary": { + "state": "released", + "field": { + "value": "Your care team has released the following information for today.", + "context": { + "release": { + "state": "released", + "released_at": "2026-07-26T14:58:00Z", + "content_policy_version": "nightingale-patient-language.v0-candidate" + }, + "freshness": { + "status": "current", + "observed_at": "2026-07-26T14:55:00Z", + "patient_notice": null + }, + "uncertainty": { + "level": "low", + "explanation": "This is the latest released information available for this item.", + "can_change": true + }, + "language": { + "locale": "en-US", + "release_state": "approved-source-language", + "plain_language_review": "approved" + }, + "correction": { + "state": "original", + "patient_notice": null + }, + "offline": { + "availability": "online-only", + "durable_storage_permitted": false + } + } + } + }, + "schedule": { + "state": "released", + "items": [ + { + "item_handle": "ntg_tdyitem_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "label": { + "value": "Morning test", + "context": { + "release": { + "state": "released", + "released_at": "2026-07-26T14:58:00Z", + "content_policy_version": "nightingale-patient-language.v0-candidate" + }, + "freshness": { + "status": "current", + "observed_at": "2026-07-26T14:55:00Z", + "patient_notice": null + }, + "uncertainty": { + "level": "low", + "explanation": "This is the latest released information available for this item.", + "can_change": true + }, + "language": { + "locale": "en-US", + "release_state": "approved-source-language", + "plain_language_review": "approved" + }, + "correction": { + "state": "original", + "patient_notice": null + }, + "offline": { + "availability": "online-only", + "durable_storage_permitted": false + } + } + }, + "detail": { + "value": "Your care team will explain what to expect before the test.", + "context": { + "release": { + "state": "released", + "released_at": "2026-07-26T14:58:00Z", + "content_policy_version": "nightingale-patient-language.v0-candidate" + }, + "freshness": { + "status": "current", + "observed_at": "2026-07-26T14:55:00Z", + "patient_notice": null + }, + "uncertainty": { + "level": "low", + "explanation": "This is the latest released information available for this item.", + "can_change": true + }, + "language": { + "locale": "en-US", + "release_state": "approved-source-language", + "plain_language_review": "approved" + }, + "correction": { + "state": "original", + "patient_notice": null + }, + "offline": { + "availability": "online-only", + "durable_storage_permitted": false + } + } + }, + "status": { + "value": "delayed", + "context": { + "release": { + "state": "released", + "released_at": "2026-07-26T14:58:00Z", + "content_policy_version": "nightingale-patient-language.v0-candidate" + }, + "freshness": { + "status": "current", + "observed_at": "2026-07-26T14:55:00Z", + "patient_notice": null + }, + "uncertainty": { + "level": "high", + "explanation": "This information may change as your care team learns more.", + "can_change": true + }, + "language": { + "locale": "en-US", + "release_state": "approved-source-language", + "plain_language_review": "approved" + }, + "correction": { + "state": "original", + "patient_notice": null + }, + "offline": { + "availability": "online-only", + "durable_storage_permitted": false + } + } + }, + "time_window": { + "value": "Your care team is reviewing the timing.", + "context": { + "release": { + "state": "released", + "released_at": "2026-07-26T14:58:00Z", + "content_policy_version": "nightingale-patient-language.v0-candidate" + }, + "freshness": { + "status": "current", + "observed_at": "2026-07-26T14:55:00Z", + "patient_notice": null + }, + "uncertainty": { + "level": "high", + "explanation": "This information may change as your care team learns more.", + "can_change": true + }, + "language": { + "locale": "en-US", + "release_state": "approved-source-language", + "plain_language_review": "approved" + }, + "correction": { + "state": "original", + "patient_notice": null + }, + "offline": { + "availability": "online-only", + "durable_storage_permitted": false + } + } + }, + "timing_confidence": { + "value": "estimated", + "context": { + "release": { + "state": "released", + "released_at": "2026-07-26T14:58:00Z", + "content_policy_version": "nightingale-patient-language.v0-candidate" + }, + "freshness": { + "status": "current", + "observed_at": "2026-07-26T14:55:00Z", + "patient_notice": null + }, + "uncertainty": { + "level": "medium", + "explanation": "This information may change as your care team learns more.", + "can_change": true + }, + "language": { + "locale": "en-US", + "release_state": "approved-source-language", + "plain_language_review": "approved" + }, + "correction": { + "state": "original", + "patient_notice": null + }, + "offline": { + "availability": "online-only", + "durable_storage_permitted": false + } + } + }, + "preparation": { + "value": "Please ask your nurse whether you need to prepare.", + "context": { + "release": { + "state": "released", + "released_at": "2026-07-26T14:58:00Z", + "content_policy_version": "nightingale-patient-language.v0-candidate" + }, + "freshness": { + "status": "current", + "observed_at": "2026-07-26T14:55:00Z", + "patient_notice": null + }, + "uncertainty": { + "level": "medium", + "explanation": "This information may change as your care team learns more.", + "can_change": true + }, + "language": { + "locale": "en-US", + "release_state": "approved-source-language", + "plain_language_review": "approved" + }, + "correction": { + "state": "original", + "patient_notice": null + }, + "offline": { + "availability": "online-only", + "durable_storage_permitted": false + } + } + }, + "can_change": { + "value": true, + "context": { + "release": { + "state": "released", + "released_at": "2026-07-26T14:58:00Z", + "content_policy_version": "nightingale-patient-language.v0-candidate" + }, + "freshness": { + "status": "current", + "observed_at": "2026-07-26T14:55:00Z", + "patient_notice": null + }, + "uncertainty": { + "level": "medium", + "explanation": "This information may change as your care team learns more.", + "can_change": true + }, + "language": { + "locale": "en-US", + "release_state": "approved-source-language", + "plain_language_review": "approved" + }, + "correction": { + "state": "original", + "patient_notice": null + }, + "offline": { + "availability": "online-only", + "durable_storage_permitted": false + } + } + } + } + ], + "patient_notice": null + }, + "next_steps": { + "state": "released", + "items": [ + { + "value": "Talk with your care team during rounds.", + "context": { + "release": { + "state": "released", + "released_at": "2026-07-26T14:58:00Z", + "content_policy_version": "nightingale-patient-language.v0-candidate" + }, + "freshness": { + "status": "current", + "observed_at": "2026-07-26T14:55:00Z", + "patient_notice": null + }, + "uncertainty": { + "level": "low", + "explanation": "This is the latest released information available for this item.", + "can_change": true + }, + "language": { + "locale": "en-US", + "release_state": "approved-source-language", + "plain_language_review": "approved" + }, + "correction": { + "state": "original", + "patient_notice": null + }, + "offline": { + "availability": "online-only", + "durable_storage_permitted": false + } + } + } + ], + "patient_notice": null + }, + "care_location": { + "state": "released", + "fields": { + "facility_display_name": { + "value": "Acumenus Medical Center", + "context": { + "release": { + "state": "released", + "released_at": "2026-07-26T14:58:00Z", + "content_policy_version": "nightingale-patient-language.v0-candidate" + }, + "freshness": { + "status": "current", + "observed_at": "2026-07-26T14:55:00Z", + "patient_notice": null + }, + "uncertainty": { + "level": "low", + "explanation": "This is the latest released information available for this item.", + "can_change": true + }, + "language": { + "locale": "en-US", + "release_state": "approved-source-language", + "plain_language_review": "approved" + }, + "correction": { + "state": "original", + "patient_notice": null + }, + "offline": { + "availability": "online-only", + "durable_storage_permitted": false + } + } + }, + "unit_display_name": { + "value": "Medical unit", + "context": { + "release": { + "state": "released", + "released_at": "2026-07-26T14:58:00Z", + "content_policy_version": "nightingale-patient-language.v0-candidate" + }, + "freshness": { + "status": "current", + "observed_at": "2026-07-26T14:55:00Z", + "patient_notice": null + }, + "uncertainty": { + "level": "low", + "explanation": "This is the latest released information available for this item.", + "can_change": true + }, + "language": { + "locale": "en-US", + "release_state": "approved-source-language", + "plain_language_review": "approved" + }, + "correction": { + "state": "original", + "patient_notice": null + }, + "offline": { + "availability": "online-only", + "durable_storage_permitted": false + } + } + }, + "room_display_name": { + "value": "Your current room", + "context": { + "release": { + "state": "released", + "released_at": "2026-07-26T14:58:00Z", + "content_policy_version": "nightingale-patient-language.v0-candidate" + }, + "freshness": { + "status": "current", + "observed_at": "2026-07-26T14:55:00Z", + "patient_notice": null + }, + "uncertainty": { + "level": "low", + "explanation": "This is the latest released information available for this item.", + "can_change": true + }, + "language": { + "locale": "en-US", + "release_state": "approved-source-language", + "plain_language_review": "approved" + }, + "correction": { + "state": "original", + "patient_notice": null + }, + "offline": { + "availability": "online-only", + "durable_storage_permitted": false + } + } + }, + "status": { + "value": "current", + "context": { + "release": { + "state": "released", + "released_at": "2026-07-26T14:58:00Z", + "content_policy_version": "nightingale-patient-language.v0-candidate" + }, + "freshness": { + "status": "current", + "observed_at": "2026-07-26T14:55:00Z", + "patient_notice": null + }, + "uncertainty": { + "level": "low", + "explanation": "This is the latest released information available for this item.", + "can_change": true + }, + "language": { + "locale": "en-US", + "release_state": "approved-source-language", + "plain_language_review": "approved" + }, + "correction": { + "state": "original", + "patient_notice": null + }, + "offline": { + "availability": "online-only", + "durable_storage_permitted": false + } + } + } + }, + "patient_notice": null + }, + "discharge_outlook": { + "state": "released", + "fields": { + "estimated_range": { + "value": "Your care team is still reviewing timing.", + "context": { + "release": { + "state": "released", + "released_at": "2026-07-26T14:58:00Z", + "content_policy_version": "nightingale-patient-language.v0-candidate" + }, + "freshness": { + "status": "current", + "observed_at": "2026-07-26T14:55:00Z", + "patient_notice": null + }, + "uncertainty": { + "level": "unknown", + "explanation": "This information may change as your care team learns more.", + "can_change": true + }, + "language": { + "locale": "en-US", + "release_state": "approved-source-language", + "plain_language_review": "approved" + }, + "correction": { + "state": "original", + "patient_notice": null + }, + "offline": { + "availability": "online-only", + "durable_storage_permitted": false + } + } + }, + "confidence": { + "value": "unknown", + "context": { + "release": { + "state": "released", + "released_at": "2026-07-26T14:58:00Z", + "content_policy_version": "nightingale-patient-language.v0-candidate" + }, + "freshness": { + "status": "current", + "observed_at": "2026-07-26T14:55:00Z", + "patient_notice": null + }, + "uncertainty": { + "level": "unknown", + "explanation": "This information may change as your care team learns more.", + "can_change": true + }, + "language": { + "locale": "en-US", + "release_state": "approved-source-language", + "plain_language_review": "approved" + }, + "correction": { + "state": "original", + "patient_notice": null + }, + "offline": { + "availability": "online-only", + "durable_storage_permitted": false + } + } + }, + "readiness_topics": { + "state": "released", + "items": [ + { + "value": "Safe movement and support at home.", + "context": { + "release": { + "state": "released", + "released_at": "2026-07-26T14:58:00Z", + "content_policy_version": "nightingale-patient-language.v0-candidate" + }, + "freshness": { + "status": "current", + "observed_at": "2026-07-26T14:55:00Z", + "patient_notice": null + }, + "uncertainty": { + "level": "low", + "explanation": "This is the latest released information available for this item.", + "can_change": true + }, + "language": { + "locale": "en-US", + "release_state": "approved-source-language", + "plain_language_review": "approved" + }, + "correction": { + "state": "original", + "patient_notice": null + }, + "offline": { + "availability": "online-only", + "durable_storage_permitted": false + } + } + } + ], + "patient_notice": null + }, + "remaining_steps": { + "state": "released", + "items": [ + { + "value": "Your team will review what needs to happen next.", + "context": { + "release": { + "state": "released", + "released_at": "2026-07-26T14:58:00Z", + "content_policy_version": "nightingale-patient-language.v0-candidate" + }, + "freshness": { + "status": "current", + "observed_at": "2026-07-26T14:55:00Z", + "patient_notice": null + }, + "uncertainty": { + "level": "low", + "explanation": "This is the latest released information available for this item.", + "can_change": true + }, + "language": { + "locale": "en-US", + "release_state": "approved-source-language", + "plain_language_review": "approved" + }, + "correction": { + "state": "original", + "patient_notice": null + }, + "offline": { + "availability": "online-only", + "durable_storage_permitted": false + } + } + } + ], + "patient_notice": null + }, + "can_change": { + "value": true, + "context": { + "release": { + "state": "released", + "released_at": "2026-07-26T14:58:00Z", + "content_policy_version": "nightingale-patient-language.v0-candidate" + }, + "freshness": { + "status": "current", + "observed_at": "2026-07-26T14:55:00Z", + "patient_notice": null + }, + "uncertainty": { + "level": "unknown", + "explanation": "This information may change as your care team learns more.", + "can_change": true + }, + "language": { + "locale": "en-US", + "release_state": "approved-source-language", + "plain_language_review": "approved" + }, + "correction": { + "state": "original", + "patient_notice": null + }, + "offline": { + "availability": "online-only", + "durable_storage_permitted": false + } + } + } + }, + "patient_notice": "This is not a promised discharge date. Timing can change." + }, + "questions": { + "state": "released", + "items": [ + { + "value": "What should I expect before my next planned step?", + "context": { + "release": { + "state": "released", + "released_at": "2026-07-26T14:58:00Z", + "content_policy_version": "nightingale-patient-language.v0-candidate" + }, + "freshness": { + "status": "current", + "observed_at": "2026-07-26T14:55:00Z", + "patient_notice": null + }, + "uncertainty": { + "level": "low", + "explanation": "This is the latest released information available for this item.", + "can_change": true + }, + "language": { + "locale": "en-US", + "release_state": "approved-source-language", + "plain_language_review": "approved" + }, + "correction": { + "state": "original", + "patient_notice": null + }, + "offline": { + "availability": "online-only", + "durable_storage_permitted": false + } + } + } + ], + "patient_notice": null + }, + "notices": { + "state": "released", + "items": [ + { + "value": "Care plans can change. Ask your care team if this does not match what you were told.", + "context": { + "release": { + "state": "released", + "released_at": "2026-07-26T14:58:00Z", + "content_policy_version": "nightingale-patient-language.v0-candidate" + }, + "freshness": { + "status": "current", + "observed_at": "2026-07-26T14:55:00Z", + "patient_notice": null + }, + "uncertainty": { + "level": "low", + "explanation": "This is the latest released information available for this item.", + "can_change": true + }, + "language": { + "locale": "en-US", + "release_state": "approved-source-language", + "plain_language_review": "approved" + }, + "correction": { + "state": "original", + "patient_notice": null + }, + "offline": { + "availability": "online-only", + "durable_storage_permitted": false + } + } + } + ], + "patient_notice": null + } + }, + "revision_notice": null + }, + "meta": { + "request_id": "ntg_req_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "generated_at": "2026-07-26T15:00:00Z", + "policy_version": "nightingale-today-policy.v0-candidate", + "authorization_evaluated_at": "2026-07-26T15:00:00Z", + "inpatient_context_evaluated_at": "2026-07-26T15:00:00Z", + "locale": "en-US", + "completeness": "governed-evaluation-complete" + }, + "links": {} + } + }, + "success_discharge_unknown_timing": { + "status": 200, + "headers": { + "Cache-Control": "private, no-store, max-age=0", + "Pragma": "no-cache", + "X-Robots-Tag": "noindex, nofollow, noarchive", + "Vary": "Authorization, Accept-Language", + "Content-Type": "application/json" + }, + "body": { + "data": { + "kind": "today", + "content_revision": "ntg_tdyrev_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "sections": { + "headline": { + "state": "released", + "field": { + "value": "Your care plan for today", + "context": { + "release": { + "state": "released", + "released_at": "2026-07-26T14:58:00Z", + "content_policy_version": "nightingale-patient-language.v0-candidate" + }, + "freshness": { + "status": "current", + "observed_at": "2026-07-26T14:55:00Z", + "patient_notice": null + }, + "uncertainty": { + "level": "low", + "explanation": "This is the latest released information available for this item.", + "can_change": true + }, + "language": { + "locale": "en-US", + "release_state": "approved-source-language", + "plain_language_review": "approved" + }, + "correction": { + "state": "original", + "patient_notice": null + }, + "offline": { + "availability": "online-only", + "durable_storage_permitted": false + } + } + } + }, + "summary": { + "state": "released", + "field": { + "value": "Your care team has released the following information for today.", + "context": { + "release": { + "state": "released", + "released_at": "2026-07-26T14:58:00Z", + "content_policy_version": "nightingale-patient-language.v0-candidate" + }, + "freshness": { + "status": "current", + "observed_at": "2026-07-26T14:55:00Z", + "patient_notice": null + }, + "uncertainty": { + "level": "low", + "explanation": "This is the latest released information available for this item.", + "can_change": true + }, + "language": { + "locale": "en-US", + "release_state": "approved-source-language", + "plain_language_review": "approved" + }, + "correction": { + "state": "original", + "patient_notice": null + }, + "offline": { + "availability": "online-only", + "durable_storage_permitted": false + } + } + } + }, + "schedule": { + "state": "released", + "items": [ + { + "item_handle": "ntg_tdyitem_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "label": { + "value": "Morning test", + "context": { + "release": { + "state": "released", + "released_at": "2026-07-26T14:58:00Z", + "content_policy_version": "nightingale-patient-language.v0-candidate" + }, + "freshness": { + "status": "current", + "observed_at": "2026-07-26T14:55:00Z", + "patient_notice": null + }, + "uncertainty": { + "level": "low", + "explanation": "This is the latest released information available for this item.", + "can_change": true + }, + "language": { + "locale": "en-US", + "release_state": "approved-source-language", + "plain_language_review": "approved" + }, + "correction": { + "state": "original", + "patient_notice": null + }, + "offline": { + "availability": "online-only", + "durable_storage_permitted": false + } + } + }, + "detail": { + "value": "Your care team will explain what to expect before the test.", + "context": { + "release": { + "state": "released", + "released_at": "2026-07-26T14:58:00Z", + "content_policy_version": "nightingale-patient-language.v0-candidate" + }, + "freshness": { + "status": "current", + "observed_at": "2026-07-26T14:55:00Z", + "patient_notice": null + }, + "uncertainty": { + "level": "low", + "explanation": "This is the latest released information available for this item.", + "can_change": true + }, + "language": { + "locale": "en-US", + "release_state": "approved-source-language", + "plain_language_review": "approved" + }, + "correction": { + "state": "original", + "patient_notice": null + }, + "offline": { + "availability": "online-only", + "durable_storage_permitted": false + } + } + }, + "status": { + "value": "planned", + "context": { + "release": { + "state": "released", + "released_at": "2026-07-26T14:58:00Z", + "content_policy_version": "nightingale-patient-language.v0-candidate" + }, + "freshness": { + "status": "current", + "observed_at": "2026-07-26T14:55:00Z", + "patient_notice": null + }, + "uncertainty": { + "level": "low", + "explanation": "This is the latest released information available for this item.", + "can_change": true + }, + "language": { + "locale": "en-US", + "release_state": "approved-source-language", + "plain_language_review": "approved" + }, + "correction": { + "state": "original", + "patient_notice": null + }, + "offline": { + "availability": "online-only", + "durable_storage_permitted": false + } + } + }, + "time_window": { + "value": "This morning", + "context": { + "release": { + "state": "released", + "released_at": "2026-07-26T14:58:00Z", + "content_policy_version": "nightingale-patient-language.v0-candidate" + }, + "freshness": { + "status": "current", + "observed_at": "2026-07-26T14:55:00Z", + "patient_notice": null + }, + "uncertainty": { + "level": "medium", + "explanation": "This information may change as your care team learns more.", + "can_change": true + }, + "language": { + "locale": "en-US", + "release_state": "approved-source-language", + "plain_language_review": "approved" + }, + "correction": { + "state": "original", + "patient_notice": null + }, + "offline": { + "availability": "online-only", + "durable_storage_permitted": false + } + } + }, + "timing_confidence": { + "value": "estimated", + "context": { + "release": { + "state": "released", + "released_at": "2026-07-26T14:58:00Z", + "content_policy_version": "nightingale-patient-language.v0-candidate" + }, + "freshness": { + "status": "current", + "observed_at": "2026-07-26T14:55:00Z", + "patient_notice": null + }, + "uncertainty": { + "level": "medium", + "explanation": "This information may change as your care team learns more.", + "can_change": true + }, + "language": { + "locale": "en-US", + "release_state": "approved-source-language", + "plain_language_review": "approved" + }, + "correction": { + "state": "original", + "patient_notice": null + }, + "offline": { + "availability": "online-only", + "durable_storage_permitted": false + } + } + }, + "preparation": { + "value": "Please ask your nurse whether you need to prepare.", + "context": { + "release": { + "state": "released", + "released_at": "2026-07-26T14:58:00Z", + "content_policy_version": "nightingale-patient-language.v0-candidate" + }, + "freshness": { + "status": "current", + "observed_at": "2026-07-26T14:55:00Z", + "patient_notice": null + }, + "uncertainty": { + "level": "medium", + "explanation": "This information may change as your care team learns more.", + "can_change": true + }, + "language": { + "locale": "en-US", + "release_state": "approved-source-language", + "plain_language_review": "approved" + }, + "correction": { + "state": "original", + "patient_notice": null + }, + "offline": { + "availability": "online-only", + "durable_storage_permitted": false + } + } + }, + "can_change": { + "value": true, + "context": { + "release": { + "state": "released", + "released_at": "2026-07-26T14:58:00Z", + "content_policy_version": "nightingale-patient-language.v0-candidate" + }, + "freshness": { + "status": "current", + "observed_at": "2026-07-26T14:55:00Z", + "patient_notice": null + }, + "uncertainty": { + "level": "medium", + "explanation": "This information may change as your care team learns more.", + "can_change": true + }, + "language": { + "locale": "en-US", + "release_state": "approved-source-language", + "plain_language_review": "approved" + }, + "correction": { + "state": "original", + "patient_notice": null + }, + "offline": { + "availability": "online-only", + "durable_storage_permitted": false + } + } + } + } + ], + "patient_notice": null + }, + "next_steps": { + "state": "released", + "items": [ + { + "value": "Talk with your care team during rounds.", + "context": { + "release": { + "state": "released", + "released_at": "2026-07-26T14:58:00Z", + "content_policy_version": "nightingale-patient-language.v0-candidate" + }, + "freshness": { + "status": "current", + "observed_at": "2026-07-26T14:55:00Z", + "patient_notice": null + }, + "uncertainty": { + "level": "low", + "explanation": "This is the latest released information available for this item.", + "can_change": true + }, + "language": { + "locale": "en-US", + "release_state": "approved-source-language", + "plain_language_review": "approved" + }, + "correction": { + "state": "original", + "patient_notice": null + }, + "offline": { + "availability": "online-only", + "durable_storage_permitted": false + } + } + } + ], + "patient_notice": null + }, + "care_location": { + "state": "released", + "fields": { + "facility_display_name": { + "value": "Acumenus Medical Center", + "context": { + "release": { + "state": "released", + "released_at": "2026-07-26T14:58:00Z", + "content_policy_version": "nightingale-patient-language.v0-candidate" + }, + "freshness": { + "status": "current", + "observed_at": "2026-07-26T14:55:00Z", + "patient_notice": null + }, + "uncertainty": { + "level": "low", + "explanation": "This is the latest released information available for this item.", + "can_change": true + }, + "language": { + "locale": "en-US", + "release_state": "approved-source-language", + "plain_language_review": "approved" + }, + "correction": { + "state": "original", + "patient_notice": null + }, + "offline": { + "availability": "online-only", + "durable_storage_permitted": false + } + } + }, + "unit_display_name": { + "value": "Medical unit", + "context": { + "release": { + "state": "released", + "released_at": "2026-07-26T14:58:00Z", + "content_policy_version": "nightingale-patient-language.v0-candidate" + }, + "freshness": { + "status": "current", + "observed_at": "2026-07-26T14:55:00Z", + "patient_notice": null + }, + "uncertainty": { + "level": "low", + "explanation": "This is the latest released information available for this item.", + "can_change": true + }, + "language": { + "locale": "en-US", + "release_state": "approved-source-language", + "plain_language_review": "approved" + }, + "correction": { + "state": "original", + "patient_notice": null + }, + "offline": { + "availability": "online-only", + "durable_storage_permitted": false + } + } + }, + "room_display_name": { + "value": "Your current room", + "context": { + "release": { + "state": "released", + "released_at": "2026-07-26T14:58:00Z", + "content_policy_version": "nightingale-patient-language.v0-candidate" + }, + "freshness": { + "status": "current", + "observed_at": "2026-07-26T14:55:00Z", + "patient_notice": null + }, + "uncertainty": { + "level": "low", + "explanation": "This is the latest released information available for this item.", + "can_change": true + }, + "language": { + "locale": "en-US", + "release_state": "approved-source-language", + "plain_language_review": "approved" + }, + "correction": { + "state": "original", + "patient_notice": null + }, + "offline": { + "availability": "online-only", + "durable_storage_permitted": false + } + } + }, + "status": { + "value": "current", + "context": { + "release": { + "state": "released", + "released_at": "2026-07-26T14:58:00Z", + "content_policy_version": "nightingale-patient-language.v0-candidate" + }, + "freshness": { + "status": "current", + "observed_at": "2026-07-26T14:55:00Z", + "patient_notice": null + }, + "uncertainty": { + "level": "low", + "explanation": "This is the latest released information available for this item.", + "can_change": true + }, + "language": { + "locale": "en-US", + "release_state": "approved-source-language", + "plain_language_review": "approved" + }, + "correction": { + "state": "original", + "patient_notice": null + }, + "offline": { + "availability": "online-only", + "durable_storage_permitted": false + } + } + } + }, + "patient_notice": null + }, + "discharge_outlook": { + "state": "released", + "fields": { + "estimated_range": { + "value": "Discharge timing is not known yet.", + "context": { + "release": { + "state": "released", + "released_at": "2026-07-26T14:58:00Z", + "content_policy_version": "nightingale-patient-language.v0-candidate" + }, + "freshness": { + "status": "current", + "observed_at": "2026-07-26T14:55:00Z", + "patient_notice": null + }, + "uncertainty": { + "level": "unknown", + "explanation": "This information may change as your care team learns more.", + "can_change": true + }, + "language": { + "locale": "en-US", + "release_state": "approved-source-language", + "plain_language_review": "approved" + }, + "correction": { + "state": "original", + "patient_notice": null + }, + "offline": { + "availability": "online-only", + "durable_storage_permitted": false + } + } + }, + "confidence": { + "value": "unknown", + "context": { + "release": { + "state": "released", + "released_at": "2026-07-26T14:58:00Z", + "content_policy_version": "nightingale-patient-language.v0-candidate" + }, + "freshness": { + "status": "current", + "observed_at": "2026-07-26T14:55:00Z", + "patient_notice": null + }, + "uncertainty": { + "level": "unknown", + "explanation": "This information may change as your care team learns more.", + "can_change": true + }, + "language": { + "locale": "en-US", + "release_state": "approved-source-language", + "plain_language_review": "approved" + }, + "correction": { + "state": "original", + "patient_notice": null + }, + "offline": { + "availability": "online-only", + "durable_storage_permitted": false + } + } + }, + "readiness_topics": { + "state": "released", + "items": [ + { + "value": "Safe movement and support at home.", + "context": { + "release": { + "state": "released", + "released_at": "2026-07-26T14:58:00Z", + "content_policy_version": "nightingale-patient-language.v0-candidate" + }, + "freshness": { + "status": "current", + "observed_at": "2026-07-26T14:55:00Z", + "patient_notice": null + }, + "uncertainty": { + "level": "low", + "explanation": "This is the latest released information available for this item.", + "can_change": true + }, + "language": { + "locale": "en-US", + "release_state": "approved-source-language", + "plain_language_review": "approved" + }, + "correction": { + "state": "original", + "patient_notice": null + }, + "offline": { + "availability": "online-only", + "durable_storage_permitted": false + } + } + } + ], + "patient_notice": null + }, + "remaining_steps": { + "state": "released", + "items": [ + { + "value": "Your team will review what needs to happen next.", + "context": { + "release": { + "state": "released", + "released_at": "2026-07-26T14:58:00Z", + "content_policy_version": "nightingale-patient-language.v0-candidate" + }, + "freshness": { + "status": "current", + "observed_at": "2026-07-26T14:55:00Z", + "patient_notice": null + }, + "uncertainty": { + "level": "low", + "explanation": "This is the latest released information available for this item.", + "can_change": true + }, + "language": { + "locale": "en-US", + "release_state": "approved-source-language", + "plain_language_review": "approved" + }, + "correction": { + "state": "original", + "patient_notice": null + }, + "offline": { + "availability": "online-only", + "durable_storage_permitted": false + } + } + } + ], + "patient_notice": null + }, + "can_change": { + "value": true, + "context": { + "release": { + "state": "released", + "released_at": "2026-07-26T14:58:00Z", + "content_policy_version": "nightingale-patient-language.v0-candidate" + }, + "freshness": { + "status": "current", + "observed_at": "2026-07-26T14:55:00Z", + "patient_notice": null + }, + "uncertainty": { + "level": "unknown", + "explanation": "This information may change as your care team learns more.", + "can_change": true + }, + "language": { + "locale": "en-US", + "release_state": "approved-source-language", + "plain_language_review": "approved" + }, + "correction": { + "state": "original", + "patient_notice": null + }, + "offline": { + "availability": "online-only", + "durable_storage_permitted": false + } + } + } + }, + "patient_notice": "This is not a promised discharge date. Timing can change." + }, + "questions": { + "state": "released", + "items": [ + { + "value": "What should I expect before my next planned step?", + "context": { + "release": { + "state": "released", + "released_at": "2026-07-26T14:58:00Z", + "content_policy_version": "nightingale-patient-language.v0-candidate" + }, + "freshness": { + "status": "current", + "observed_at": "2026-07-26T14:55:00Z", + "patient_notice": null + }, + "uncertainty": { + "level": "low", + "explanation": "This is the latest released information available for this item.", + "can_change": true + }, + "language": { + "locale": "en-US", + "release_state": "approved-source-language", + "plain_language_review": "approved" + }, + "correction": { + "state": "original", + "patient_notice": null + }, + "offline": { + "availability": "online-only", + "durable_storage_permitted": false + } + } + } + ], + "patient_notice": null + }, + "notices": { + "state": "released", + "items": [ + { + "value": "Care plans can change. Ask your care team if this does not match what you were told.", + "context": { + "release": { + "state": "released", + "released_at": "2026-07-26T14:58:00Z", + "content_policy_version": "nightingale-patient-language.v0-candidate" + }, + "freshness": { + "status": "current", + "observed_at": "2026-07-26T14:55:00Z", + "patient_notice": null + }, + "uncertainty": { + "level": "low", + "explanation": "This is the latest released information available for this item.", + "can_change": true + }, + "language": { + "locale": "en-US", + "release_state": "approved-source-language", + "plain_language_review": "approved" + }, + "correction": { + "state": "original", + "patient_notice": null + }, + "offline": { + "availability": "online-only", + "durable_storage_permitted": false + } + } + } + ], + "patient_notice": null + } + }, + "revision_notice": null + }, + "meta": { + "request_id": "ntg_req_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "generated_at": "2026-07-26T15:00:00Z", + "policy_version": "nightingale-today-policy.v0-candidate", + "authorization_evaluated_at": "2026-07-26T15:00:00Z", + "inpatient_context_evaluated_at": "2026-07-26T15:00:00Z", + "locale": "en-US", + "completeness": "governed-evaluation-complete" + }, + "links": {} + } + }, + "success_location_updating": { + "status": 200, + "headers": { + "Cache-Control": "private, no-store, max-age=0", + "Pragma": "no-cache", + "X-Robots-Tag": "noindex, nofollow, noarchive", + "Vary": "Authorization, Accept-Language", + "Content-Type": "application/json" + }, + "body": { + "data": { + "kind": "today", + "content_revision": "ntg_tdyrev_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "sections": { + "headline": { + "state": "released", + "field": { + "value": "Your care plan for today", + "context": { + "release": { + "state": "released", + "released_at": "2026-07-26T14:58:00Z", + "content_policy_version": "nightingale-patient-language.v0-candidate" + }, + "freshness": { + "status": "current", + "observed_at": "2026-07-26T14:55:00Z", + "patient_notice": null + }, + "uncertainty": { + "level": "low", + "explanation": "This is the latest released information available for this item.", + "can_change": true + }, + "language": { + "locale": "en-US", + "release_state": "approved-source-language", + "plain_language_review": "approved" + }, + "correction": { + "state": "original", + "patient_notice": null + }, + "offline": { + "availability": "online-only", + "durable_storage_permitted": false + } + } + } + }, + "summary": { + "state": "released", + "field": { + "value": "Your care team has released the following information for today.", + "context": { + "release": { + "state": "released", + "released_at": "2026-07-26T14:58:00Z", + "content_policy_version": "nightingale-patient-language.v0-candidate" + }, + "freshness": { + "status": "current", + "observed_at": "2026-07-26T14:55:00Z", + "patient_notice": null + }, + "uncertainty": { + "level": "low", + "explanation": "This is the latest released information available for this item.", + "can_change": true + }, + "language": { + "locale": "en-US", + "release_state": "approved-source-language", + "plain_language_review": "approved" + }, + "correction": { + "state": "original", + "patient_notice": null + }, + "offline": { + "availability": "online-only", + "durable_storage_permitted": false + } + } + } + }, + "schedule": { + "state": "released", + "items": [ + { + "item_handle": "ntg_tdyitem_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "label": { + "value": "Morning test", + "context": { + "release": { + "state": "released", + "released_at": "2026-07-26T14:58:00Z", + "content_policy_version": "nightingale-patient-language.v0-candidate" + }, + "freshness": { + "status": "current", + "observed_at": "2026-07-26T14:55:00Z", + "patient_notice": null + }, + "uncertainty": { + "level": "low", + "explanation": "This is the latest released information available for this item.", + "can_change": true + }, + "language": { + "locale": "en-US", + "release_state": "approved-source-language", + "plain_language_review": "approved" + }, + "correction": { + "state": "original", + "patient_notice": null + }, + "offline": { + "availability": "online-only", + "durable_storage_permitted": false + } + } + }, + "detail": { + "value": "Your care team will explain what to expect before the test.", + "context": { + "release": { + "state": "released", + "released_at": "2026-07-26T14:58:00Z", + "content_policy_version": "nightingale-patient-language.v0-candidate" + }, + "freshness": { + "status": "current", + "observed_at": "2026-07-26T14:55:00Z", + "patient_notice": null + }, + "uncertainty": { + "level": "low", + "explanation": "This is the latest released information available for this item.", + "can_change": true + }, + "language": { + "locale": "en-US", + "release_state": "approved-source-language", + "plain_language_review": "approved" + }, + "correction": { + "state": "original", + "patient_notice": null + }, + "offline": { + "availability": "online-only", + "durable_storage_permitted": false + } + } + }, + "status": { + "value": "planned", + "context": { + "release": { + "state": "released", + "released_at": "2026-07-26T14:58:00Z", + "content_policy_version": "nightingale-patient-language.v0-candidate" + }, + "freshness": { + "status": "current", + "observed_at": "2026-07-26T14:55:00Z", + "patient_notice": null + }, + "uncertainty": { + "level": "low", + "explanation": "This is the latest released information available for this item.", + "can_change": true + }, + "language": { + "locale": "en-US", + "release_state": "approved-source-language", + "plain_language_review": "approved" + }, + "correction": { + "state": "original", + "patient_notice": null + }, + "offline": { + "availability": "online-only", + "durable_storage_permitted": false + } + } + }, + "time_window": { + "value": "This morning", + "context": { + "release": { + "state": "released", + "released_at": "2026-07-26T14:58:00Z", + "content_policy_version": "nightingale-patient-language.v0-candidate" + }, + "freshness": { + "status": "current", + "observed_at": "2026-07-26T14:55:00Z", + "patient_notice": null + }, + "uncertainty": { + "level": "medium", + "explanation": "This information may change as your care team learns more.", + "can_change": true + }, + "language": { + "locale": "en-US", + "release_state": "approved-source-language", + "plain_language_review": "approved" + }, + "correction": { + "state": "original", + "patient_notice": null + }, + "offline": { + "availability": "online-only", + "durable_storage_permitted": false + } + } + }, + "timing_confidence": { + "value": "estimated", + "context": { + "release": { + "state": "released", + "released_at": "2026-07-26T14:58:00Z", + "content_policy_version": "nightingale-patient-language.v0-candidate" + }, + "freshness": { + "status": "current", + "observed_at": "2026-07-26T14:55:00Z", + "patient_notice": null + }, + "uncertainty": { + "level": "medium", + "explanation": "This information may change as your care team learns more.", + "can_change": true + }, + "language": { + "locale": "en-US", + "release_state": "approved-source-language", + "plain_language_review": "approved" + }, + "correction": { + "state": "original", + "patient_notice": null + }, + "offline": { + "availability": "online-only", + "durable_storage_permitted": false + } + } + }, + "preparation": { + "value": "Please ask your nurse whether you need to prepare.", + "context": { + "release": { + "state": "released", + "released_at": "2026-07-26T14:58:00Z", + "content_policy_version": "nightingale-patient-language.v0-candidate" + }, + "freshness": { + "status": "current", + "observed_at": "2026-07-26T14:55:00Z", + "patient_notice": null + }, + "uncertainty": { + "level": "medium", + "explanation": "This information may change as your care team learns more.", + "can_change": true + }, + "language": { + "locale": "en-US", + "release_state": "approved-source-language", + "plain_language_review": "approved" + }, + "correction": { + "state": "original", + "patient_notice": null + }, + "offline": { + "availability": "online-only", + "durable_storage_permitted": false + } + } + }, + "can_change": { + "value": true, + "context": { + "release": { + "state": "released", + "released_at": "2026-07-26T14:58:00Z", + "content_policy_version": "nightingale-patient-language.v0-candidate" + }, + "freshness": { + "status": "current", + "observed_at": "2026-07-26T14:55:00Z", + "patient_notice": null + }, + "uncertainty": { + "level": "medium", + "explanation": "This information may change as your care team learns more.", + "can_change": true + }, + "language": { + "locale": "en-US", + "release_state": "approved-source-language", + "plain_language_review": "approved" + }, + "correction": { + "state": "original", + "patient_notice": null + }, + "offline": { + "availability": "online-only", + "durable_storage_permitted": false + } + } + } + } + ], + "patient_notice": null + }, + "next_steps": { + "state": "released", + "items": [ + { + "value": "Talk with your care team during rounds.", + "context": { + "release": { + "state": "released", + "released_at": "2026-07-26T14:58:00Z", + "content_policy_version": "nightingale-patient-language.v0-candidate" + }, + "freshness": { + "status": "current", + "observed_at": "2026-07-26T14:55:00Z", + "patient_notice": null + }, + "uncertainty": { + "level": "low", + "explanation": "This is the latest released information available for this item.", + "can_change": true + }, + "language": { + "locale": "en-US", + "release_state": "approved-source-language", + "plain_language_review": "approved" + }, + "correction": { + "state": "original", + "patient_notice": null + }, + "offline": { + "availability": "online-only", + "durable_storage_permitted": false + } + } + } + ], + "patient_notice": null + }, + "care_location": { + "state": "released", + "fields": { + "facility_display_name": { + "value": "Acumenus Medical Center", + "context": { + "release": { + "state": "released", + "released_at": "2026-07-26T14:58:00Z", + "content_policy_version": "nightingale-patient-language.v0-candidate" + }, + "freshness": { + "status": "current", + "observed_at": "2026-07-26T14:55:00Z", + "patient_notice": null + }, + "uncertainty": { + "level": "low", + "explanation": "This is the latest released information available for this item.", + "can_change": true + }, + "language": { + "locale": "en-US", + "release_state": "approved-source-language", + "plain_language_review": "approved" + }, + "correction": { + "state": "original", + "patient_notice": null + }, + "offline": { + "availability": "online-only", + "durable_storage_permitted": false + } + } + }, + "unit_display_name": { + "value": "Medical unit", + "context": { + "release": { + "state": "released", + "released_at": "2026-07-26T14:58:00Z", + "content_policy_version": "nightingale-patient-language.v0-candidate" + }, + "freshness": { + "status": "current", + "observed_at": "2026-07-26T14:55:00Z", + "patient_notice": null + }, + "uncertainty": { + "level": "low", + "explanation": "This is the latest released information available for this item.", + "can_change": true + }, + "language": { + "locale": "en-US", + "release_state": "approved-source-language", + "plain_language_review": "approved" + }, + "correction": { + "state": "original", + "patient_notice": null + }, + "offline": { + "availability": "online-only", + "durable_storage_permitted": false + } + } + }, + "room_display_name": { + "value": "Your care location is being updated.", + "context": { + "release": { + "state": "released", + "released_at": "2026-07-26T14:58:00Z", + "content_policy_version": "nightingale-patient-language.v0-candidate" + }, + "freshness": { + "status": "current", + "observed_at": "2026-07-26T14:55:00Z", + "patient_notice": null + }, + "uncertainty": { + "level": "medium", + "explanation": "This information may change as your care team learns more.", + "can_change": true + }, + "language": { + "locale": "en-US", + "release_state": "approved-source-language", + "plain_language_review": "approved" + }, + "correction": { + "state": "original", + "patient_notice": null + }, + "offline": { + "availability": "online-only", + "durable_storage_permitted": false + } + } + }, + "status": { + "value": "updating", + "context": { + "release": { + "state": "released", + "released_at": "2026-07-26T14:58:00Z", + "content_policy_version": "nightingale-patient-language.v0-candidate" + }, + "freshness": { + "status": "current", + "observed_at": "2026-07-26T14:55:00Z", + "patient_notice": null + }, + "uncertainty": { + "level": "medium", + "explanation": "This information may change as your care team learns more.", + "can_change": true + }, + "language": { + "locale": "en-US", + "release_state": "approved-source-language", + "plain_language_review": "approved" + }, + "correction": { + "state": "original", + "patient_notice": null + }, + "offline": { + "availability": "online-only", + "durable_storage_permitted": false + } + } + } + }, + "patient_notice": null + }, + "discharge_outlook": { + "state": "released", + "fields": { + "estimated_range": { + "value": "Your care team is still reviewing timing.", + "context": { + "release": { + "state": "released", + "released_at": "2026-07-26T14:58:00Z", + "content_policy_version": "nightingale-patient-language.v0-candidate" + }, + "freshness": { + "status": "current", + "observed_at": "2026-07-26T14:55:00Z", + "patient_notice": null + }, + "uncertainty": { + "level": "unknown", + "explanation": "This information may change as your care team learns more.", + "can_change": true + }, + "language": { + "locale": "en-US", + "release_state": "approved-source-language", + "plain_language_review": "approved" + }, + "correction": { + "state": "original", + "patient_notice": null + }, + "offline": { + "availability": "online-only", + "durable_storage_permitted": false + } + } + }, + "confidence": { + "value": "unknown", + "context": { + "release": { + "state": "released", + "released_at": "2026-07-26T14:58:00Z", + "content_policy_version": "nightingale-patient-language.v0-candidate" + }, + "freshness": { + "status": "current", + "observed_at": "2026-07-26T14:55:00Z", + "patient_notice": null + }, + "uncertainty": { + "level": "unknown", + "explanation": "This information may change as your care team learns more.", + "can_change": true + }, + "language": { + "locale": "en-US", + "release_state": "approved-source-language", + "plain_language_review": "approved" + }, + "correction": { + "state": "original", + "patient_notice": null + }, + "offline": { + "availability": "online-only", + "durable_storage_permitted": false + } + } + }, + "readiness_topics": { + "state": "released", + "items": [ + { + "value": "Safe movement and support at home.", + "context": { + "release": { + "state": "released", + "released_at": "2026-07-26T14:58:00Z", + "content_policy_version": "nightingale-patient-language.v0-candidate" + }, + "freshness": { + "status": "current", + "observed_at": "2026-07-26T14:55:00Z", + "patient_notice": null + }, + "uncertainty": { + "level": "low", + "explanation": "This is the latest released information available for this item.", + "can_change": true + }, + "language": { + "locale": "en-US", + "release_state": "approved-source-language", + "plain_language_review": "approved" + }, + "correction": { + "state": "original", + "patient_notice": null + }, + "offline": { + "availability": "online-only", + "durable_storage_permitted": false + } + } + } + ], + "patient_notice": null + }, + "remaining_steps": { + "state": "released", + "items": [ + { + "value": "Your team will review what needs to happen next.", + "context": { + "release": { + "state": "released", + "released_at": "2026-07-26T14:58:00Z", + "content_policy_version": "nightingale-patient-language.v0-candidate" + }, + "freshness": { + "status": "current", + "observed_at": "2026-07-26T14:55:00Z", + "patient_notice": null + }, + "uncertainty": { + "level": "low", + "explanation": "This is the latest released information available for this item.", + "can_change": true + }, + "language": { + "locale": "en-US", + "release_state": "approved-source-language", + "plain_language_review": "approved" + }, + "correction": { + "state": "original", + "patient_notice": null + }, + "offline": { + "availability": "online-only", + "durable_storage_permitted": false + } + } + } + ], + "patient_notice": null + }, + "can_change": { + "value": true, + "context": { + "release": { + "state": "released", + "released_at": "2026-07-26T14:58:00Z", + "content_policy_version": "nightingale-patient-language.v0-candidate" + }, + "freshness": { + "status": "current", + "observed_at": "2026-07-26T14:55:00Z", + "patient_notice": null + }, + "uncertainty": { + "level": "unknown", + "explanation": "This information may change as your care team learns more.", + "can_change": true + }, + "language": { + "locale": "en-US", + "release_state": "approved-source-language", + "plain_language_review": "approved" + }, + "correction": { + "state": "original", + "patient_notice": null + }, + "offline": { + "availability": "online-only", + "durable_storage_permitted": false + } + } + } + }, + "patient_notice": "This is not a promised discharge date. Timing can change." + }, + "questions": { + "state": "released", + "items": [ + { + "value": "What should I expect before my next planned step?", + "context": { + "release": { + "state": "released", + "released_at": "2026-07-26T14:58:00Z", + "content_policy_version": "nightingale-patient-language.v0-candidate" + }, + "freshness": { + "status": "current", + "observed_at": "2026-07-26T14:55:00Z", + "patient_notice": null + }, + "uncertainty": { + "level": "low", + "explanation": "This is the latest released information available for this item.", + "can_change": true + }, + "language": { + "locale": "en-US", + "release_state": "approved-source-language", + "plain_language_review": "approved" + }, + "correction": { + "state": "original", + "patient_notice": null + }, + "offline": { + "availability": "online-only", + "durable_storage_permitted": false + } + } + } + ], + "patient_notice": null + }, + "notices": { + "state": "released", + "items": [ + { + "value": "Care plans can change. Ask your care team if this does not match what you were told.", + "context": { + "release": { + "state": "released", + "released_at": "2026-07-26T14:58:00Z", + "content_policy_version": "nightingale-patient-language.v0-candidate" + }, + "freshness": { + "status": "current", + "observed_at": "2026-07-26T14:55:00Z", + "patient_notice": null + }, + "uncertainty": { + "level": "low", + "explanation": "This is the latest released information available for this item.", + "can_change": true + }, + "language": { + "locale": "en-US", + "release_state": "approved-source-language", + "plain_language_review": "approved" + }, + "correction": { + "state": "original", + "patient_notice": null + }, + "offline": { + "availability": "online-only", + "durable_storage_permitted": false + } + } + } + ], + "patient_notice": null + } + }, + "revision_notice": null + }, + "meta": { + "request_id": "ntg_req_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "generated_at": "2026-07-26T15:00:00Z", + "policy_version": "nightingale-today-policy.v0-candidate", + "authorization_evaluated_at": "2026-07-26T15:00:00Z", + "inpatient_context_evaluated_at": "2026-07-26T15:00:00Z", + "locale": "en-US", + "completeness": "governed-evaluation-complete" + }, + "links": {} + } + }, + "success_distinct_field_contexts": { + "status": 200, + "headers": { + "Cache-Control": "private, no-store, max-age=0", + "Pragma": "no-cache", + "X-Robots-Tag": "noindex, nofollow, noarchive", + "Vary": "Authorization, Accept-Language", + "Content-Type": "application/json" + }, + "body": { + "data": { + "kind": "today", + "content_revision": "ntg_tdyrev_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "sections": { + "headline": { + "state": "released", + "field": { + "value": "Your care plan for today", + "context": { + "release": { + "state": "released", + "released_at": "2026-07-26T14:58:00Z", + "content_policy_version": "nightingale-patient-language.v0-candidate" + }, + "freshness": { + "status": "current", + "observed_at": "2026-07-26T14:55:00Z", + "patient_notice": null + }, + "uncertainty": { + "level": "low", + "explanation": "This is the latest released information available for this item.", + "can_change": true + }, + "language": { + "locale": "en-US", + "release_state": "approved-source-language", + "plain_language_review": "approved" + }, + "correction": { + "state": "original", + "patient_notice": null + }, + "offline": { + "availability": "online-only", + "durable_storage_permitted": false + } + } + } + }, + "summary": { + "state": "released", + "field": { + "value": "Your care team has released the following information for today.", + "context": { + "release": { + "state": "released", + "released_at": "2026-07-26T14:58:00Z", + "content_policy_version": "nightingale-patient-language.v0-candidate" + }, + "freshness": { + "status": "current", + "observed_at": "2026-07-26T14:55:00Z", + "patient_notice": null + }, + "uncertainty": { + "level": "low", + "explanation": "This is the latest released information available for this item.", + "can_change": true + }, + "language": { + "locale": "en-US", + "release_state": "approved-source-language", + "plain_language_review": "approved" + }, + "correction": { + "state": "original", + "patient_notice": null + }, + "offline": { + "availability": "online-only", + "durable_storage_permitted": false + } + } + } + }, + "schedule": { + "state": "released", + "items": [ + { + "item_handle": "ntg_tdyitem_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "label": { + "value": "Morning test", + "context": { + "release": { + "state": "released", + "released_at": "2026-07-26T14:58:00Z", + "content_policy_version": "nightingale-patient-language.v0-candidate" + }, + "freshness": { + "status": "current", + "observed_at": "2026-07-26T14:55:00Z", + "patient_notice": null + }, + "uncertainty": { + "level": "low", + "explanation": "This is the latest released information available for this item.", + "can_change": true + }, + "language": { + "locale": "en-US", + "release_state": "approved-source-language", + "plain_language_review": "approved" + }, + "correction": { + "state": "original", + "patient_notice": null + }, + "offline": { + "availability": "online-only", + "durable_storage_permitted": false + } + } + }, + "detail": { + "value": "Your care team will explain what to expect before the test.", + "context": { + "release": { + "state": "released", + "released_at": "2026-07-26T14:58:00Z", + "content_policy_version": "nightingale-patient-language.v0-candidate" + }, + "freshness": { + "status": "current", + "observed_at": "2026-07-26T14:55:00Z", + "patient_notice": null + }, + "uncertainty": { + "level": "low", + "explanation": "This is the latest released information available for this item.", + "can_change": true + }, + "language": { + "locale": "en-US", + "release_state": "approved-source-language", + "plain_language_review": "approved" + }, + "correction": { + "state": "original", + "patient_notice": null + }, + "offline": { + "availability": "online-only", + "durable_storage_permitted": false + } + } + }, + "status": { + "value": "planned", + "context": { + "release": { + "state": "released", + "released_at": "2026-07-26T14:58:00Z", + "content_policy_version": "nightingale-patient-language.v0-candidate" + }, + "freshness": { + "status": "current", + "observed_at": "2026-07-26T14:55:00Z", + "patient_notice": null + }, + "uncertainty": { + "level": "low", + "explanation": "This is the latest released information available for this item.", + "can_change": true + }, + "language": { + "locale": "en-US", + "release_state": "approved-source-language", + "plain_language_review": "approved" + }, + "correction": { + "state": "original", + "patient_notice": null + }, + "offline": { + "availability": "online-only", + "durable_storage_permitted": false + } + } + }, + "time_window": { + "value": "This morning", + "context": { + "release": { + "state": "released", + "released_at": "2026-07-26T12:05:00Z", + "content_policy_version": "nightingale-patient-language.v0-candidate" + }, + "freshness": { + "status": "approved-stale", + "observed_at": "2026-07-26T12:00:00Z", + "patient_notice": "This time window has not been refreshed recently and may have changed. Ask your care team before relying on it." + }, + "uncertainty": { + "level": "high", + "explanation": "This information may change as your care team learns more.", + "can_change": true + }, + "language": { + "locale": "en-US", + "release_state": "approved-source-language", + "plain_language_review": "approved" + }, + "correction": { + "state": "original", + "patient_notice": null + }, + "offline": { + "availability": "online-only", + "durable_storage_permitted": false + } + } + }, + "timing_confidence": { + "value": "estimated", + "context": { + "release": { + "state": "released", + "released_at": "2026-07-26T14:58:00Z", + "content_policy_version": "nightingale-patient-language.v0-candidate" + }, + "freshness": { + "status": "current", + "observed_at": "2026-07-26T14:55:00Z", + "patient_notice": null + }, + "uncertainty": { + "level": "medium", + "explanation": "This information may change as your care team learns more.", + "can_change": true + }, + "language": { + "locale": "en-US", + "release_state": "approved-source-language", + "plain_language_review": "approved" + }, + "correction": { + "state": "original", + "patient_notice": null + }, + "offline": { + "availability": "online-only", + "durable_storage_permitted": false + } + } + }, + "preparation": { + "value": "Please ask your nurse whether you need to prepare.", + "context": { + "release": { + "state": "released", + "released_at": "2026-07-26T14:58:00Z", + "content_policy_version": "nightingale-patient-language.v0-candidate" + }, + "freshness": { + "status": "current", + "observed_at": "2026-07-26T14:55:00Z", + "patient_notice": null + }, + "uncertainty": { + "level": "medium", + "explanation": "This information may change as your care team learns more.", + "can_change": true + }, + "language": { + "locale": "en-US", + "release_state": "approved-translation", + "plain_language_review": "approved" + }, + "correction": { + "state": "original", + "patient_notice": null + }, + "offline": { + "availability": "online-only", + "durable_storage_permitted": false + } + } + }, + "can_change": { + "value": true, + "context": { + "release": { + "state": "released", + "released_at": "2026-07-26T14:58:00Z", + "content_policy_version": "nightingale-patient-language.v0-candidate" + }, + "freshness": { + "status": "current", + "observed_at": "2026-07-26T14:55:00Z", + "patient_notice": null + }, + "uncertainty": { + "level": "medium", + "explanation": "This information may change as your care team learns more.", + "can_change": true + }, + "language": { + "locale": "en-US", + "release_state": "approved-source-language", + "plain_language_review": "approved" + }, + "correction": { + "state": "original", + "patient_notice": null + }, + "offline": { + "availability": "online-only", + "durable_storage_permitted": false + } + } + } + } + ], + "patient_notice": null + }, + "next_steps": { + "state": "released", + "items": [ + { + "value": "Talk with your care team during rounds.", + "context": { + "release": { + "state": "released", + "released_at": "2026-07-26T14:58:00Z", + "content_policy_version": "nightingale-patient-language.v0-candidate" + }, + "freshness": { + "status": "current", + "observed_at": "2026-07-26T14:55:00Z", + "patient_notice": null + }, + "uncertainty": { + "level": "medium", + "explanation": "This information may change as your care team learns more.", + "can_change": true + }, + "language": { + "locale": "en-US", + "release_state": "approved-source-language", + "plain_language_review": "approved" + }, + "correction": { + "state": "corrected", + "patient_notice": "Your care team updated this information. Please use the details shown here." + }, + "offline": { + "availability": "online-only", + "durable_storage_permitted": false + } + } + } + ], + "patient_notice": null + }, + "care_location": { + "state": "released", + "fields": { + "facility_display_name": { + "value": "Acumenus Medical Center", + "context": { + "release": { + "state": "released", + "released_at": "2026-07-26T14:58:00Z", + "content_policy_version": "nightingale-patient-language.v0-candidate" + }, + "freshness": { + "status": "current", + "observed_at": "2026-07-26T14:55:00Z", + "patient_notice": null + }, + "uncertainty": { + "level": "low", + "explanation": "This is the latest released information available for this item.", + "can_change": true + }, + "language": { + "locale": "en-US", + "release_state": "approved-source-language", + "plain_language_review": "approved" + }, + "correction": { + "state": "original", + "patient_notice": null + }, + "offline": { + "availability": "online-only", + "durable_storage_permitted": false + } + } + }, + "unit_display_name": { + "value": "Medical unit", + "context": { + "release": { + "state": "released", + "released_at": "2026-07-26T14:58:00Z", + "content_policy_version": "nightingale-patient-language.v0-candidate" + }, + "freshness": { + "status": "current", + "observed_at": "2026-07-26T14:55:00Z", + "patient_notice": null + }, + "uncertainty": { + "level": "low", + "explanation": "This is the latest released information available for this item.", + "can_change": true + }, + "language": { + "locale": "en-US", + "release_state": "approved-source-language", + "plain_language_review": "approved" + }, + "correction": { + "state": "original", + "patient_notice": null + }, + "offline": { + "availability": "online-only", + "durable_storage_permitted": false + } + } + }, + "room_display_name": { + "value": "Your current room", + "context": { + "release": { + "state": "released", + "released_at": "2026-07-26T14:58:00Z", + "content_policy_version": "nightingale-patient-language.v0-candidate" + }, + "freshness": { + "status": "current", + "observed_at": "2026-07-26T14:55:00Z", + "patient_notice": null + }, + "uncertainty": { + "level": "low", + "explanation": "This is the latest released information available for this item.", + "can_change": true + }, + "language": { + "locale": "en-US", + "release_state": "approved-source-language", + "plain_language_review": "approved" + }, + "correction": { + "state": "original", + "patient_notice": null + }, + "offline": { + "availability": "online-only", + "durable_storage_permitted": false + } + } + }, + "status": { + "value": "current", + "context": { + "release": { + "state": "released", + "released_at": "2026-07-26T14:58:00Z", + "content_policy_version": "nightingale-patient-language.v0-candidate" + }, + "freshness": { + "status": "current", + "observed_at": "2026-07-26T14:55:00Z", + "patient_notice": null + }, + "uncertainty": { + "level": "low", + "explanation": "This is the latest released information available for this item.", + "can_change": true + }, + "language": { + "locale": "en-US", + "release_state": "approved-source-language", + "plain_language_review": "approved" + }, + "correction": { + "state": "original", + "patient_notice": null + }, + "offline": { + "availability": "online-only", + "durable_storage_permitted": false + } + } + } + }, + "patient_notice": null + }, + "discharge_outlook": { + "state": "released", + "fields": { + "estimated_range": { + "value": "Your care team is still reviewing timing.", + "context": { + "release": { + "state": "released", + "released_at": "2026-07-26T14:58:00Z", + "content_policy_version": "nightingale-patient-language.v0-candidate" + }, + "freshness": { + "status": "current", + "observed_at": "2026-07-26T14:55:00Z", + "patient_notice": null + }, + "uncertainty": { + "level": "unknown", + "explanation": "This information may change as your care team learns more.", + "can_change": true + }, + "language": { + "locale": "en-US", + "release_state": "approved-source-language", + "plain_language_review": "approved" + }, + "correction": { + "state": "original", + "patient_notice": null + }, + "offline": { + "availability": "online-only", + "durable_storage_permitted": false + } + } + }, + "confidence": { + "value": "unknown", + "context": { + "release": { + "state": "released", + "released_at": "2026-07-26T14:58:00Z", + "content_policy_version": "nightingale-patient-language.v0-candidate" + }, + "freshness": { + "status": "current", + "observed_at": "2026-07-26T14:55:00Z", + "patient_notice": null + }, + "uncertainty": { + "level": "unknown", + "explanation": "This information may change as your care team learns more.", + "can_change": true + }, + "language": { + "locale": "en-US", + "release_state": "approved-source-language", + "plain_language_review": "approved" + }, + "correction": { + "state": "original", + "patient_notice": null + }, + "offline": { + "availability": "online-only", + "durable_storage_permitted": false + } + } + }, + "readiness_topics": { + "state": "released", + "items": [ + { + "value": "Safe movement and support at home.", + "context": { + "release": { + "state": "released", + "released_at": "2026-07-26T14:58:00Z", + "content_policy_version": "nightingale-patient-language.v0-candidate" + }, + "freshness": { + "status": "current", + "observed_at": "2026-07-26T14:55:00Z", + "patient_notice": null + }, + "uncertainty": { + "level": "low", + "explanation": "This is the latest released information available for this item.", + "can_change": true + }, + "language": { + "locale": "en-US", + "release_state": "approved-source-language", + "plain_language_review": "approved" + }, + "correction": { + "state": "original", + "patient_notice": null + }, + "offline": { + "availability": "online-only", + "durable_storage_permitted": false + } + } + } + ], + "patient_notice": null + }, + "remaining_steps": { + "state": "released", + "items": [ + { + "value": "Your team will review what needs to happen next.", + "context": { + "release": { + "state": "released", + "released_at": "2026-07-26T14:58:00Z", + "content_policy_version": "nightingale-patient-language.v0-candidate" + }, + "freshness": { + "status": "current", + "observed_at": "2026-07-26T14:55:00Z", + "patient_notice": null + }, + "uncertainty": { + "level": "low", + "explanation": "This is the latest released information available for this item.", + "can_change": true + }, + "language": { + "locale": "en-US", + "release_state": "approved-source-language", + "plain_language_review": "approved" + }, + "correction": { + "state": "original", + "patient_notice": null + }, + "offline": { + "availability": "online-only", + "durable_storage_permitted": false + } + } + } + ], + "patient_notice": null + }, + "can_change": { + "value": true, + "context": { + "release": { + "state": "released", + "released_at": "2026-07-26T14:58:00Z", + "content_policy_version": "nightingale-patient-language.v0-candidate" + }, + "freshness": { + "status": "current", + "observed_at": "2026-07-26T14:55:00Z", + "patient_notice": null + }, + "uncertainty": { + "level": "unknown", + "explanation": "This information may change as your care team learns more.", + "can_change": true + }, + "language": { + "locale": "en-US", + "release_state": "approved-source-language", + "plain_language_review": "approved" + }, + "correction": { + "state": "original", + "patient_notice": null + }, + "offline": { + "availability": "online-only", + "durable_storage_permitted": false + } + } + } + }, + "patient_notice": "This is not a promised discharge date. Timing can change." + }, + "questions": { + "state": "released", + "items": [ + { + "value": "What should I expect before my next planned step?", + "context": { + "release": { + "state": "released", + "released_at": "2026-07-26T14:58:00Z", + "content_policy_version": "nightingale-patient-language.v0-candidate" + }, + "freshness": { + "status": "current", + "observed_at": "2026-07-26T14:55:00Z", + "patient_notice": null + }, + "uncertainty": { + "level": "low", + "explanation": "This is the latest released information available for this item.", + "can_change": true + }, + "language": { + "locale": "en-US", + "release_state": "approved-source-language", + "plain_language_review": "approved" + }, + "correction": { + "state": "original", + "patient_notice": null + }, + "offline": { + "availability": "online-only", + "durable_storage_permitted": false + } + } + } + ], + "patient_notice": null + }, + "notices": { + "state": "released", + "items": [ + { + "value": "Care plans can change. Ask your care team if this does not match what you were told.", + "context": { + "release": { + "state": "released", + "released_at": "2026-07-26T14:58:00Z", + "content_policy_version": "nightingale-patient-language.v0-candidate" + }, + "freshness": { + "status": "current", + "observed_at": "2026-07-26T14:55:00Z", + "patient_notice": null + }, + "uncertainty": { + "level": "low", + "explanation": "This is the latest released information available for this item.", + "can_change": true + }, + "language": { + "locale": "en-US", + "release_state": "approved-source-language", + "plain_language_review": "approved" + }, + "correction": { + "state": "original", + "patient_notice": null + }, + "offline": { + "availability": "online-only", + "durable_storage_permitted": false + } + } + } + ], + "patient_notice": null + } + }, + "revision_notice": null + }, + "meta": { + "request_id": "ntg_req_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "generated_at": "2026-07-26T15:00:00Z", + "policy_version": "nightingale-today-policy.v0-candidate", + "authorization_evaluated_at": "2026-07-26T15:00:00Z", + "inpatient_context_evaluated_at": "2026-07-26T15:00:00Z", + "locale": "en-US", + "completeness": "governed-evaluation-complete" + }, + "links": {} + } + }, + "not_found": { + "status": 404, + "headers": { + "Cache-Control": "private, no-store, max-age=0", + "Pragma": "no-cache", + "X-Robots-Tag": "noindex, nofollow, noarchive", + "Vary": "Authorization, Accept-Language", + "Content-Type": "application/json" + }, + "body": { + "data": null, + "error": { + "code": "not_found", + "message": "This experience is not available." + }, + "meta": { + "request_id": "ntg_req_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "generated_at": "2026-07-26T15:00:00Z", + "policy_version": "nightingale-today-policy.v0-candidate", + "authorization_evaluated_at": "2026-07-26T15:00:00Z", + "inpatient_context_evaluated_at": "2026-07-26T15:00:00Z", + "locale": "en-US", + "completeness": "withheld" + }, + "links": {} + } + }, + "authentication_required": { + "status": 401, + "headers": { + "Cache-Control": "private, no-store, max-age=0", + "Pragma": "no-cache", + "X-Robots-Tag": "noindex, nofollow, noarchive", + "Vary": "Authorization, Accept-Language", + "Content-Type": "application/json" + }, + "body": { + "data": null, + "error": { + "code": "authentication_required", + "message": "Sign in is required." + }, + "meta": { + "request_id": "ntg_req_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "generated_at": "2026-07-26T15:00:00Z", + "policy_version": "nightingale-today-policy.v0-candidate", + "authorization_evaluated_at": "2026-07-26T15:00:00Z", + "inpatient_context_evaluated_at": "2026-07-26T15:00:00Z", + "locale": "en-US", + "completeness": "withheld" + }, + "links": {} + } + }, + "access_unavailable": { + "status": 403, + "headers": { + "Cache-Control": "private, no-store, max-age=0", + "Pragma": "no-cache", + "X-Robots-Tag": "noindex, nofollow, noarchive", + "Vary": "Authorization, Accept-Language", + "Content-Type": "application/json" + }, + "body": { + "data": null, + "error": { + "code": "access_unavailable", + "message": "This experience is not available for this account." + }, + "meta": { + "request_id": "ntg_req_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "generated_at": "2026-07-26T15:00:00Z", + "policy_version": "nightingale-today-policy.v0-candidate", + "authorization_evaluated_at": "2026-07-26T15:00:00Z", + "inpatient_context_evaluated_at": "2026-07-26T15:00:00Z", + "locale": "en-US", + "completeness": "withheld" + }, + "links": {} + } + }, + "account_state_requires_review": { + "status": 409, + "headers": { + "Cache-Control": "private, no-store, max-age=0", + "Pragma": "no-cache", + "X-Robots-Tag": "noindex, nofollow, noarchive", + "Vary": "Authorization, Accept-Language", + "Content-Type": "application/json" + }, + "body": { + "data": null, + "error": { + "code": "account_state_requires_review", + "message": "We could not safely confirm the hospital stay for this request. Please ask your care team for help." + }, + "meta": { + "request_id": "ntg_req_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "generated_at": "2026-07-26T15:00:00Z", + "policy_version": "nightingale-today-policy.v0-candidate", + "authorization_evaluated_at": "2026-07-26T15:00:00Z", + "inpatient_context_evaluated_at": "2026-07-26T15:00:00Z", + "locale": "en-US", + "completeness": "withheld" + }, + "links": {} + } + }, + "rate_limited": { + "status": 429, + "headers": { + "Cache-Control": "private, no-store, max-age=0", + "Pragma": "no-cache", + "X-Robots-Tag": "noindex, nofollow, noarchive", + "Vary": "Authorization, Accept-Language", + "Content-Type": "application/json" + }, + "body": { + "data": null, + "error": { + "code": "rate_limited", + "message": "Too many requests were submitted. Please try again later." + }, + "meta": { + "request_id": "ntg_req_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "generated_at": "2026-07-26T15:00:00Z", + "policy_version": "nightingale-today-policy.v0-candidate", + "authorization_evaluated_at": "2026-07-26T15:00:00Z", + "inpatient_context_evaluated_at": "2026-07-26T15:00:00Z", + "locale": "en-US", + "completeness": "withheld" + }, + "links": {} + } + }, + "temporarily_unavailable": { + "status": 503, + "headers": { + "Cache-Control": "private, no-store, max-age=0", + "Pragma": "no-cache", + "X-Robots-Tag": "noindex, nofollow, noarchive", + "Vary": "Authorization, Accept-Language", + "Content-Type": "application/json" + }, + "body": { + "data": null, + "error": { + "code": "temporarily_unavailable", + "message": "Today’s released information is temporarily unavailable. Please ask your care team if you need help now." + }, + "meta": { + "request_id": "ntg_req_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "generated_at": "2026-07-26T15:00:00Z", + "policy_version": "nightingale-today-policy.v0-candidate", + "authorization_evaluated_at": "2026-07-26T15:00:00Z", + "inpatient_context_evaluated_at": "2026-07-26T15:00:00Z", + "locale": "en-US", + "completeness": "evaluation-incomplete" + }, + "links": {} + } + } + }, + "cases": [ + { + "case_id": "current_full", + "category": "allow", + "preconditions": [ + "all gates pass and every Today section has governed released content" + ], + "expected_template": "success_full_current", + "audit_mode": "durable-evaluation-and-disclosure" + }, + { + "case_id": "current_minimal", + "category": "allow", + "preconditions": [ + "mandatory headline and summary are released while optional sections are explicitly unavailable" + ], + "expected_template": "success_minimal", + "audit_mode": "durable-evaluation-and-disclosure" + }, + { + "case_id": "schedule_released_empty", + "category": "allow", + "preconditions": [ + "authoritative evaluation completed and no schedule items were released" + ], + "expected_template": "success_schedule_empty", + "audit_mode": "durable-evaluation-and-disclosure" + }, + { + "case_id": "optional_sections_not_available", + "category": "allow", + "preconditions": [ + "care location and discharge outlook are explicitly unavailable without implying absence" + ], + "expected_template": "success_optional_unavailable", + "audit_mode": "durable-evaluation-and-disclosure" + }, + { + "case_id": "approved_stale_with_notice", + "category": "allow", + "preconditions": [ + "field-specific stale use is clinically approved and carries a patient notice" + ], + "expected_template": "success_approved_stale", + "audit_mode": "durable-evaluation-and-disclosure" + }, + { + "case_id": "corrected_replacement", + "category": "allow", + "preconditions": [ + "a corrected replacement release is effective and the withdrawn value is not serialized" + ], + "expected_template": "success_correction", + "audit_mode": "durable-evaluation-and-disclosure" + }, + { + "case_id": "approved_translation", + "category": "allow", + "preconditions": [ + "every displayed field has an approved translation for the response locale" + ], + "expected_template": "success_approved_translation", + "audit_mode": "durable-evaluation-and-disclosure" + }, + { + "case_id": "approved_source_language", + "category": "allow", + "preconditions": [ + "the approved source language matches the response locale" + ], + "expected_template": "success_approved_source_language", + "audit_mode": "durable-evaluation-and-disclosure" + }, + { + "case_id": "delayed_schedule_high_uncertainty", + "category": "allow", + "preconditions": [ + "a delayed item uses approved patient language and field-specific high uncertainty" + ], + "expected_template": "success_delayed_high_uncertainty", + "audit_mode": "durable-evaluation-and-disclosure" + }, + { + "case_id": "discharge_outlook_unknown_timing", + "category": "allow", + "preconditions": [ + "the discharge outlook explicitly communicates unknown timing and that it can change" + ], + "expected_template": "success_discharge_unknown_timing", + "audit_mode": "durable-evaluation-and-disclosure" + }, + { + "case_id": "care_location_updating", + "category": "allow", + "preconditions": [ + "location status is released as updating without asserting an unconfirmed destination" + ], + "expected_template": "success_location_updating", + "audit_mode": "durable-evaluation-and-disclosure" + }, + { + "case_id": "distinct_field_contexts", + "category": "allow", + "preconditions": [ + "each displayed field retains its own release, freshness, uncertainty, language, correction, and offline context" + ], + "expected_template": "success_distinct_field_contexts", + "audit_mode": "durable-evaluation-and-disclosure" + }, + { + "case_id": "product_disabled", + "category": "release-gate", + "preconditions": [ + "Nightingale product activation is false" + ], + "expected_template": "not_found", + "audit_mode": "best-effort-indistinguishable-denial" + }, + { + "case_id": "operation_disabled", + "category": "release-gate", + "preconditions": [ + "Today operation activation is false" + ], + "expected_template": "not_found", + "audit_mode": "best-effort-indistinguishable-denial" + }, + { + "case_id": "facility_disabled", + "category": "release-gate", + "preconditions": [ + "the facility is outside the approved Nightingale rollout" + ], + "expected_template": "not_found", + "audit_mode": "best-effort-indistinguishable-denial" + }, + { + "case_id": "cohort_ineligible", + "category": "authorization", + "preconditions": [ + "the authenticated person is outside the approved inpatient cohort" + ], + "expected_template": "access_unavailable", + "audit_mode": "best-effort-indistinguishable-denial" + }, + { + "case_id": "authentication_missing", + "category": "identity", + "preconditions": [ + "no approved Nightingale session is present" + ], + "expected_template": "authentication_required", + "audit_mode": "best-effort-indistinguishable-denial" + }, + { + "case_id": "wrong_identity_realm", + "category": "identity", + "preconditions": [ + "a staff or legacy patient credential is presented" + ], + "expected_template": "access_unavailable", + "audit_mode": "best-effort-indistinguishable-denial" + }, + { + "case_id": "principal_inactive", + "category": "identity", + "preconditions": [ + "the Nightingale principal is inactive" + ], + "expected_template": "access_unavailable", + "audit_mode": "best-effort-indistinguishable-denial" + }, + { + "case_id": "session_missing", + "category": "identity", + "preconditions": [ + "identity is known but no active request session exists" + ], + "expected_template": "authentication_required", + "audit_mode": "best-effort-indistinguishable-denial" + }, + { + "case_id": "session_expired", + "category": "identity", + "preconditions": [ + "the Nightingale session is expired" + ], + "expected_template": "authentication_required", + "audit_mode": "best-effort-indistinguishable-denial" + }, + { + "case_id": "session_revoked", + "category": "identity", + "preconditions": [ + "the Nightingale session is revoked" + ], + "expected_template": "authentication_required", + "audit_mode": "best-effort-indistinguishable-denial" + }, + { + "case_id": "identity_link_missing", + "category": "identity", + "preconditions": [ + "no verified self identity link is available" + ], + "expected_template": "access_unavailable", + "audit_mode": "best-effort-indistinguishable-denial" + }, + { + "case_id": "identity_link_revoked", + "category": "identity", + "preconditions": [ + "the self identity link is revoked" + ], + "expected_template": "access_unavailable", + "audit_mode": "best-effort-indistinguishable-denial" + }, + { + "case_id": "wrong_principal_handle", + "category": "non-disclosure", + "preconditions": [ + "the opaque context handle belongs to a different principal" + ], + "expected_template": "not_found", + "audit_mode": "best-effort-indistinguishable-denial" + }, + { + "case_id": "unknown_handle", + "category": "non-disclosure", + "preconditions": [ + "the opaque context handle is not known" + ], + "expected_template": "not_found", + "audit_mode": "best-effort-indistinguishable-denial" + }, + { + "case_id": "malformed_handle", + "category": "non-disclosure", + "preconditions": [ + "the context handle fails the Nightingale format" + ], + "expected_template": "not_found", + "audit_mode": "best-effort-indistinguishable-denial" + }, + { + "case_id": "representative_relationship_held", + "category": "authorization", + "preconditions": [ + "representative access has not been approved" + ], + "expected_template": "access_unavailable", + "audit_mode": "best-effort-indistinguishable-denial" + }, + { + "case_id": "sensitive_encounter_held", + "category": "authorization", + "preconditions": [ + "the encounter requires a sensitive-service policy that is not approved" + ], + "expected_template": "access_unavailable", + "audit_mode": "best-effort-indistinguishable-denial" + }, + { + "case_id": "current_context_closed", + "category": "lifecycle", + "preconditions": [ + "the authoritative inpatient context is closed before disclosure" + ], + "expected_template": "not_found", + "audit_mode": "best-effort-indistinguishable-denial" + }, + { + "case_id": "current_context_changed", + "category": "race", + "preconditions": [ + "the context becomes ineligible during the pre-serialization recheck" + ], + "expected_template": "not_found", + "audit_mode": "best-effort-indistinguishable-denial" + }, + { + "case_id": "handle_mapping_inconsistent", + "category": "integrity", + "preconditions": [ + "the handle mapping is missing, duplicated, or points to inconsistent context state" + ], + "expected_template": "account_state_requires_review", + "audit_mode": "best-effort-safety-failure" + }, + { + "case_id": "source_unavailable", + "category": "dependency", + "preconditions": [ + "the approved current-inpatient or projection source is unavailable" + ], + "expected_template": "temporarily_unavailable", + "audit_mode": "best-effort-safety-failure" + }, + { + "case_id": "database_unavailable", + "category": "dependency", + "preconditions": [ + "the governed projection store is unavailable" + ], + "expected_template": "temporarily_unavailable", + "audit_mode": "best-effort-safety-failure" + }, + { + "case_id": "today_capability_denied", + "category": "authorization", + "preconditions": [ + "server-side authorization denies the Today capability" + ], + "expected_template": "not_found", + "audit_mode": "best-effort-indistinguishable-denial" + }, + { + "case_id": "no_released_projection", + "category": "release", + "preconditions": [ + "no released Today projection exists" + ], + "expected_template": "not_found", + "audit_mode": "best-effort-indistinguishable-denial" + }, + { + "case_id": "draft_projection", + "category": "release", + "preconditions": [ + "only a draft Today projection exists" + ], + "expected_template": "not_found", + "audit_mode": "best-effort-indistinguishable-denial" + }, + { + "case_id": "future_release", + "category": "release", + "preconditions": [ + "the Today release is not yet effective" + ], + "expected_template": "not_found", + "audit_mode": "best-effort-indistinguishable-denial" + }, + { + "case_id": "retracted_projection", + "category": "release", + "preconditions": [ + "the effective Today release is retracted" + ], + "expected_template": "not_found", + "audit_mode": "best-effort-indistinguishable-denial" + }, + { + "case_id": "correction_without_replacement", + "category": "release", + "preconditions": [ + "a correction withdraws the target but no effective replacement is released" + ], + "expected_template": "not_found", + "audit_mode": "best-effort-indistinguishable-denial" + }, + { + "case_id": "conflicting_active_releases", + "category": "integrity", + "preconditions": [ + "more than one effective release claims the same sequence or authority" + ], + "expected_template": "temporarily_unavailable", + "audit_mode": "best-effort-safety-failure" + }, + { + "case_id": "policy_version_mismatch", + "category": "release", + "preconditions": [ + "the projection and active Nightingale release policy versions do not match" + ], + "expected_template": "temporarily_unavailable", + "audit_mode": "best-effort-safety-failure" + }, + { + "case_id": "clinical_approval_missing", + "category": "release", + "preconditions": [ + "clinical approval is absent for a patient-visible field" + ], + "expected_template": "not_found", + "audit_mode": "best-effort-indistinguishable-denial" + }, + { + "case_id": "patient_language_approval_missing", + "category": "language", + "preconditions": [ + "plain-language approval is absent for a displayed field" + ], + "expected_template": "temporarily_unavailable", + "audit_mode": "best-effort-safety-failure" + }, + { + "case_id": "translation_approval_missing", + "category": "language", + "preconditions": [ + "the requested-locale translation is not approved" + ], + "expected_template": "temporarily_unavailable", + "audit_mode": "best-effort-safety-failure" + }, + { + "case_id": "locale_mismatch", + "category": "language", + "preconditions": [ + "field locale and response locale differ without an approved fallback" + ], + "expected_template": "temporarily_unavailable", + "audit_mode": "best-effort-safety-failure" + }, + { + "case_id": "freshness_decision_missing", + "category": "freshness", + "preconditions": [ + "a displayed field has no approved freshness decision" + ], + "expected_template": "temporarily_unavailable", + "audit_mode": "best-effort-safety-failure" + }, + { + "case_id": "unapproved_stale_content", + "category": "freshness", + "preconditions": [ + "a stale field lacks an approved stale-use policy and patient notice" + ], + "expected_template": "temporarily_unavailable", + "audit_mode": "best-effort-safety-failure" + }, + { + "case_id": "unknown_freshness", + "category": "freshness", + "preconditions": [ + "mandatory content has unknown freshness" + ], + "expected_template": "temporarily_unavailable", + "audit_mode": "best-effort-safety-failure" + }, + { + "case_id": "mandatory_headline_missing", + "category": "schema", + "preconditions": [ + "the released document has no governed headline" + ], + "expected_template": "temporarily_unavailable", + "audit_mode": "best-effort-safety-failure" + }, + { + "case_id": "mandatory_summary_missing", + "category": "schema", + "preconditions": [ + "the released document has no governed summary" + ], + "expected_template": "temporarily_unavailable", + "audit_mode": "best-effort-safety-failure" + }, + { + "case_id": "ungoverned_field", + "category": "schema", + "preconditions": [ + "a patient-visible value lacks its complete field context" + ], + "expected_template": "temporarily_unavailable", + "audit_mode": "best-effort-safety-failure" + }, + { + "case_id": "aggregate_context_only", + "category": "schema", + "preconditions": [ + "freshness, uncertainty, or provenance exists only at document level" + ], + "expected_template": "temporarily_unavailable", + "audit_mode": "best-effort-safety-failure" + }, + { + "case_id": "internal_identifier_present", + "category": "content-safety", + "preconditions": [ + "content or context contains an internal, source, grant, encounter, staff, or patient identifier" + ], + "expected_template": "temporarily_unavailable", + "audit_mode": "best-effort-safety-failure" + }, + { + "case_id": "staff_only_content_present", + "category": "content-safety", + "preconditions": [ + "content includes staff-only prose, prioritization, disagreement, or an unreleased result" + ], + "expected_template": "temporarily_unavailable", + "audit_mode": "best-effort-safety-failure" + }, + { + "case_id": "invalid_schedule_status", + "category": "vocabulary", + "preconditions": [ + "a schedule status is outside the approved Nightingale patient vocabulary" + ], + "expected_template": "temporarily_unavailable", + "audit_mode": "best-effort-safety-failure" + }, + { + "case_id": "invalid_timing_confidence", + "category": "vocabulary", + "preconditions": [ + "timing confidence is outside the approved patient vocabulary" + ], + "expected_template": "temporarily_unavailable", + "audit_mode": "best-effort-safety-failure" + }, + { + "case_id": "invalid_item_handle", + "category": "identifier", + "preconditions": [ + "a schedule item handle fails the Nightingale format" + ], + "expected_template": "temporarily_unavailable", + "audit_mode": "best-effort-safety-failure" + }, + { + "case_id": "duplicate_item_handle", + "category": "identifier", + "preconditions": [ + "two schedule entries use the same item handle" + ], + "expected_template": "temporarily_unavailable", + "audit_mode": "best-effort-safety-failure" + }, + { + "case_id": "invalid_section_state", + "category": "schema", + "preconditions": [ + "an optional section has an unknown availability state" + ], + "expected_template": "temporarily_unavailable", + "audit_mode": "best-effort-safety-failure" + }, + { + "case_id": "released_empty_with_items", + "category": "schema", + "preconditions": [ + "a released-empty section also contains items" + ], + "expected_template": "temporarily_unavailable", + "audit_mode": "best-effort-safety-failure" + }, + { + "case_id": "not_available_with_content", + "category": "schema", + "preconditions": [ + "a not-available section also contains patient content" + ], + "expected_template": "temporarily_unavailable", + "audit_mode": "best-effort-safety-failure" + }, + { + "case_id": "inconsistent_timestamps", + "category": "integrity", + "preconditions": [ + "observed, released, generated, or authorization timestamps are not coherently ordered" + ], + "expected_template": "temporarily_unavailable", + "audit_mode": "best-effort-safety-failure" + }, + { + "case_id": "correction_actor_exposed", + "category": "content-safety", + "preconditions": [ + "a correction notice exposes an actor, reason, target identifier, or withdrawn value" + ], + "expected_template": "temporarily_unavailable", + "audit_mode": "best-effort-safety-failure" + }, + { + "case_id": "request_audit_unavailable", + "category": "audit", + "preconditions": [ + "durable request evaluation audit cannot be recorded" + ], + "expected_template": "temporarily_unavailable", + "audit_mode": "best-effort-safety-failure" + }, + { + "case_id": "disclosure_audit_unavailable", + "category": "audit", + "preconditions": [ + "durable field disclosure audit cannot be recorded" + ], + "expected_template": "temporarily_unavailable", + "audit_mode": "best-effort-safety-failure" + }, + { + "case_id": "response_serialization_failure", + "category": "integrity", + "preconditions": [ + "the governed response cannot be serialized atomically" + ], + "expected_template": "temporarily_unavailable", + "audit_mode": "best-effort-safety-failure" + }, + { + "case_id": "rate_limited", + "category": "ingress", + "preconditions": [ + "the approved principal-specific rate limit is exceeded" + ], + "expected_template": "rate_limited", + "audit_mode": "best-effort-indistinguishable-denial" + } + ] +} diff --git a/docs/nightingale/api-contract/nightingale-foundation.v0.json b/docs/nightingale/api-contract/nightingale-foundation.v0.json new file mode 100644 index 00000000..979ec9f9 --- /dev/null +++ b/docs/nightingale/api-contract/nightingale-foundation.v0.json @@ -0,0 +1,68 @@ +{ + "openapi": "3.1.1", + "info": { + "title": "Nightingale API Governance Foundation", + "summary": "A deliberately non-runnable contract ownership artifact.", + "description": "This foundation contains no API operations. It reserves a product-owned namespace for collision prevention but does not register a route or authorize a client, identity flow, patient disclosure, mutation, feature activation, source query, or deployment.", + "version": "0.0.0-governance" + }, + "x-nightingale-contract": { + "product": "Nightingale", + "audience": "inpatients-and-approved-representatives", + "governance_status": "foundation-no-operations", + "artifact_owner": "Patient Experience Platform", + "backend_boundary_owner": "Zephyrus Patient Experience Boundary", + "compatibility_input_only": true, + "route_namespace_reserved": true, + "route_namespace": "/api/nightingale/v1", + "compatibility_strategy": "independent-no-alias-no-proxy-no-redirect", + "client_generation_permitted": false + }, + "x-nightingale-activation": { + "default": "disabled", + "routes_registered": false, + "route_registration_permitted": false, + "network_clients_permitted": false, + "identity_enabled": false, + "inpatient_source_enabled": false, + "production_source_query_permitted": false, + "clinical_approval_recorded": false, + "patient_content_released": false, + "feature_activated": false, + "pilot_enrollment_confirmed": false, + "source_connector_deployed": false, + "patient_disclosure_enabled": false, + "patient_mutation_enabled": false, + "production_enabled": false, + "required_before_first_operation": [ + "approved-operation-owner", + "authorization-and-nondisclosure-tests", + "identity-and-recovery-decision", + "institutional-clinical-approval-record", + "patient-content-release-record", + "independent-feature-activation-record", + "expiry-bound-pilot-enrollment", + "approved-source-connector-deployment", + "privacy-and-security-review", + "patient-language-and-accessibility-review", + "release-and-rollback-plan" + ] + }, + "x-nightingale-identifier-policy": { + "permitted": "opaque-nightingale-product-handles-only", + "source-system-identifiers": "prohibited", + "embedded-source-meaning": "prohibited", + "cross-product-identifiers": "prohibited" + }, + "servers": [ + { + "url": "https://nightingale-api.invalid", + "description": "Reserved non-routable placeholder; this artifact has no operations." + } + ], + "security": [], + "tags": [], + "paths": {}, + "webhooks": {}, + "components": {} +} diff --git a/docs/nightingale/identity/candidates/v0/candidate.json b/docs/nightingale/identity/candidates/v0/candidate.json new file mode 100644 index 00000000..18dee4ba --- /dev/null +++ b/docs/nightingale/identity/candidates/v0/candidate.json @@ -0,0 +1,152 @@ +{ + "artifact_kind": "nightingale-nonrunnable-identity-candidate", + "candidate_id": "nightingale.identity-session-recovery.v0-candidate", + "status": "held-no-provider-no-credential", + "purpose": "Define fail-closed identity, session, representative, and recovery evaluation states without selecting an identity provider or credential design.", + "runtime": { + "route": null, + "operation_id": null, + "provider": null, + "authentication_method": null, + "credential_format": null, + "refresh_credential": null, + "enrollment_channel": null, + "recovery_channel": null, + "service_binding_permitted": false, + "client_implementation_permitted": false + }, + "activation": { + "identity": false, + "enrollment": false, + "session": false, + "recovery": false, + "representatives": false, + "nonproduction_integration": false, + "production": false + }, + "audience": { + "initial_relationship": "self", + "representative_access": "held", + "staff_realm_accepted": false, + "legacy_patient_realm_accepted": false + }, + "identity_states": { + "unavailable": { + "permits_governed_evaluation": false, + "authorizes_patient_access": false + }, + "denied": { + "permits_governed_evaluation": false, + "authorizes_patient_access": false + }, + "verified_self": { + "permits_governed_evaluation": true, + "authorizes_patient_access": false + } + }, + "positive_state_prerequisites": [ + "approved-product-and-identity-policy-version", + "approved-provider-evaluation-complete", + "issuer-audience-client-and-replay-controls-valid", + "required-assurance-and-step-up-satisfied", + "active-self-principal-in-nightingale-realm", + "active-request-scoped-session-owned-by-principal", + "verified-current-unmerged-identity-link-owned-by-principal", + "no-recovery-or-representative-transition-in-progress", + "durable-identity-evaluation-audit-committed", + "authorization-still-required-after-identity" + ], + "protected_state": { + "access_credential_persistence": "prohibited", + "durable_device_identifier": "prohibited", + "future_binding_descriptor": "dormant-no-production-caller", + "backup_or_cross_product_migration": "prohibited" + }, + "recovery_invariants": [ + "no-trust-derived-from-old-local-state", + "clear-old-local-binding-before-replacement", + "independent-proof-required", + "superseded-sessions-must-be-revoked", + "account-existence-must-not-be-disclosed", + "ambiguous-remote-outcome-must-not-be-called-success", + "patient-support-path-required-before-activation" + ], + "required_fixture_case_ids": [ + "verified_self_all_prerequisites", + "product_disabled", + "identity_disabled", + "provider_unconfigured", + "provider_unavailable", + "provider_timeout", + "provider_response_malformed", + "authentication_evidence_missing", + "authentication_evidence_invalid", + "authentication_evidence_expired", + "issuer_mismatch", + "audience_mismatch", + "client_mismatch", + "nonce_mismatch", + "replay_detected", + "authentication_method_unsupported", + "assurance_missing", + "assurance_below_policy", + "step_up_incomplete", + "principal_missing", + "wrong_identity_realm", + "staff_principal_rejected", + "representative_principal_held", + "principal_pending", + "principal_inactive", + "principal_locked", + "principal_suspended", + "principal_closed", + "principal_state_inconsistent", + "session_missing", + "session_expired", + "session_idle_expired", + "session_revoked", + "session_wrong_principal", + "session_wrong_realm", + "session_family_reuse_detected", + "session_risk_hold", + "session_state_unknown", + "session_changed_during_evaluation", + "identity_link_missing", + "identity_link_pending", + "identity_link_ambiguous", + "identity_link_merged", + "identity_link_revoked", + "identity_link_verification_time_missing", + "identity_link_wrong_principal", + "identity_link_duplicate_active", + "identity_link_assurance_mismatch", + "recovery_requested", + "recovery_evidence_incomplete", + "recovery_old_binding_not_cleared", + "recovery_superseded_session_active", + "recovery_fresh_proof_complete", + "legal_representative_held", + "guardian_held", + "caregiver_held", + "proxy_held", + "other_relationship_held", + "evaluation_audit_unavailable", + "correlation_context_malformed", + "session_limit_policy_unresolved", + "clock_untrusted", + "policy_version_mismatch", + "identity_changed_before_handoff" + ], + "approval_gates": [ + "named-identity-security-privacy-legal-accessibility-patient-support-and-release-owners", + "provider-and-assurance-contract", + "enrollment-authority-channel-and-rate-policy", + "session-expiry-rotation-device-risk-and-revocation-policy", + "recovery-lost-shared-device-and-support-policy", + "representative-minor-sensitive-service-and-relationship-policy", + "credential-and-protected-state-threat-model", + "authorization-nondisclosure-audit-and-race-tests", + "patient-language-accessibility-and-support-review", + "default-off-nonproduction-integration-and-rollback" + ] +} diff --git a/docs/nightingale/identity/candidates/v0/fixtures.json b/docs/nightingale/identity/candidates/v0/fixtures.json new file mode 100644 index 00000000..fcc533c9 --- /dev/null +++ b/docs/nightingale/identity/candidates/v0/fixtures.json @@ -0,0 +1,571 @@ +{ + "fixture_schema": "nightingale.identity-session-recovery.candidate-fixtures.v0", + "candidate_id": "nightingale.identity-session-recovery.v0-candidate", + "synthetic_only": true, + "production_replay_permitted": false, + "contains_credentials": false, + "contains_source_identifiers": false, + "result_templates": { + "verified_self": { + "identity_state": "verified_self", + "permits_governed_evaluation": true, + "authorizes_patient_access": false + }, + "denied": { + "identity_state": "denied", + "permits_governed_evaluation": false, + "authorizes_patient_access": false + }, + "unavailable": { + "identity_state": "unavailable", + "permits_governed_evaluation": false, + "authorizes_patient_access": false + } + }, + "cases": [ + { + "case_id": "verified_self_all_prerequisites", + "category": "positive", + "preconditions": [ + "Every positive-state prerequisite is satisfied with self relationship." + ], + "expected_result": "verified_self", + "audit_mode": "durable_evaluation" + }, + { + "case_id": "product_disabled", + "category": "activation", + "preconditions": ["The Nightingale product gate is disabled."], + "expected_result": "unavailable", + "audit_mode": "best_effort_unavailable" + }, + { + "case_id": "identity_disabled", + "category": "activation", + "preconditions": [ + "The Nightingale identity capability is disabled." + ], + "expected_result": "unavailable", + "audit_mode": "best_effort_unavailable" + }, + { + "case_id": "provider_unconfigured", + "category": "dependency", + "preconditions": [ + "No approved identity provider adapter is configured." + ], + "expected_result": "unavailable", + "audit_mode": "best_effort_unavailable" + }, + { + "case_id": "provider_unavailable", + "category": "dependency", + "preconditions": [ + "The approved provider dependency cannot be reached." + ], + "expected_result": "unavailable", + "audit_mode": "best_effort_unavailable" + }, + { + "case_id": "provider_timeout", + "category": "dependency", + "preconditions": [ + "Provider evaluation does not complete within its governed deadline." + ], + "expected_result": "unavailable", + "audit_mode": "best_effort_unavailable" + }, + { + "case_id": "provider_response_malformed", + "category": "integrity", + "preconditions": [ + "Provider evidence cannot be parsed or validated exactly." + ], + "expected_result": "unavailable", + "audit_mode": "durable_safety_failure" + }, + { + "case_id": "authentication_evidence_missing", + "category": "evidence", + "preconditions": [ + "No request-scoped authentication evidence is present." + ], + "expected_result": "denied", + "audit_mode": "best_effort_denial" + }, + { + "case_id": "authentication_evidence_invalid", + "category": "evidence", + "preconditions": [ + "Authentication evidence fails cryptographic or semantic validation." + ], + "expected_result": "denied", + "audit_mode": "best_effort_denial" + }, + { + "case_id": "authentication_evidence_expired", + "category": "evidence", + "preconditions": [ + "Authentication evidence is outside its approved validity window." + ], + "expected_result": "denied", + "audit_mode": "best_effort_denial" + }, + { + "case_id": "issuer_mismatch", + "category": "evidence", + "preconditions": [ + "The evidence issuer does not match the approved policy." + ], + "expected_result": "denied", + "audit_mode": "best_effort_denial" + }, + { + "case_id": "audience_mismatch", + "category": "evidence", + "preconditions": ["The evidence audience is not Nightingale."], + "expected_result": "denied", + "audit_mode": "best_effort_denial" + }, + { + "case_id": "client_mismatch", + "category": "evidence", + "preconditions": [ + "The evidence is bound to a different approved client." + ], + "expected_result": "denied", + "audit_mode": "best_effort_denial" + }, + { + "case_id": "nonce_mismatch", + "category": "evidence", + "preconditions": ["The request-bound replay value does not match."], + "expected_result": "denied", + "audit_mode": "best_effort_denial" + }, + { + "case_id": "replay_detected", + "category": "evidence", + "preconditions": [ + "Validated evidence has already been consumed or replayed." + ], + "expected_result": "denied", + "audit_mode": "durable_safety_failure" + }, + { + "case_id": "authentication_method_unsupported", + "category": "policy", + "preconditions": [ + "The presented method is not in the approved method registry." + ], + "expected_result": "denied", + "audit_mode": "best_effort_denial" + }, + { + "case_id": "assurance_missing", + "category": "assurance", + "preconditions": [ + "The provider result has no evaluable assurance statement." + ], + "expected_result": "unavailable", + "audit_mode": "durable_safety_failure" + }, + { + "case_id": "assurance_below_policy", + "category": "assurance", + "preconditions": [ + "The evaluated assurance does not satisfy the current policy." + ], + "expected_result": "denied", + "audit_mode": "best_effort_denial" + }, + { + "case_id": "step_up_incomplete", + "category": "assurance", + "preconditions": ["A required step-up evaluation is incomplete."], + "expected_result": "denied", + "audit_mode": "best_effort_denial" + }, + { + "case_id": "principal_missing", + "category": "principal", + "preconditions": [ + "No Nightingale principal can be resolved after provider validation." + ], + "expected_result": "denied", + "audit_mode": "best_effort_denial" + }, + { + "case_id": "wrong_identity_realm", + "category": "principal", + "preconditions": [ + "The resolved principal belongs to a non-Nightingale realm." + ], + "expected_result": "denied", + "audit_mode": "best_effort_denial" + }, + { + "case_id": "staff_principal_rejected", + "category": "principal", + "preconditions": [ + "The resolved subject is a staff operational principal." + ], + "expected_result": "denied", + "audit_mode": "best_effort_denial" + }, + { + "case_id": "representative_principal_held", + "category": "representative", + "preconditions": [ + "The principal is representative-scoped while representative access is held." + ], + "expected_result": "denied", + "audit_mode": "best_effort_denial" + }, + { + "case_id": "principal_pending", + "category": "principal", + "preconditions": ["The Nightingale principal has pending status."], + "expected_result": "denied", + "audit_mode": "best_effort_denial" + }, + { + "case_id": "principal_inactive", + "category": "principal", + "preconditions": ["The Nightingale principal is not active."], + "expected_result": "denied", + "audit_mode": "best_effort_denial" + }, + { + "case_id": "principal_locked", + "category": "principal", + "preconditions": ["The Nightingale principal is locked."], + "expected_result": "denied", + "audit_mode": "best_effort_denial" + }, + { + "case_id": "principal_suspended", + "category": "principal", + "preconditions": ["The Nightingale principal is suspended."], + "expected_result": "denied", + "audit_mode": "best_effort_denial" + }, + { + "case_id": "principal_closed", + "category": "principal", + "preconditions": ["The Nightingale principal is closed."], + "expected_result": "denied", + "audit_mode": "best_effort_denial" + }, + { + "case_id": "principal_state_inconsistent", + "category": "integrity", + "preconditions": [ + "Principal status and activation facts contradict one another." + ], + "expected_result": "unavailable", + "audit_mode": "durable_safety_failure" + }, + { + "case_id": "session_missing", + "category": "session", + "preconditions": [ + "No active request-scoped Nightingale session is present." + ], + "expected_result": "denied", + "audit_mode": "best_effort_denial" + }, + { + "case_id": "session_expired", + "category": "session", + "preconditions": [ + "The Nightingale session absolute lifetime has expired." + ], + "expected_result": "denied", + "audit_mode": "best_effort_denial" + }, + { + "case_id": "session_idle_expired", + "category": "session", + "preconditions": [ + "The Nightingale session idle lifetime has expired." + ], + "expected_result": "denied", + "audit_mode": "best_effort_denial" + }, + { + "case_id": "session_revoked", + "category": "session", + "preconditions": ["The Nightingale session has been revoked."], + "expected_result": "denied", + "audit_mode": "best_effort_denial" + }, + { + "case_id": "session_wrong_principal", + "category": "integrity", + "preconditions": ["The session is owned by a different principal."], + "expected_result": "unavailable", + "audit_mode": "durable_safety_failure" + }, + { + "case_id": "session_wrong_realm", + "category": "integrity", + "preconditions": [ + "The session belongs to a different product realm." + ], + "expected_result": "denied", + "audit_mode": "durable_safety_failure" + }, + { + "case_id": "session_family_reuse_detected", + "category": "session", + "preconditions": [ + "A superseded session-family artifact is replayed." + ], + "expected_result": "denied", + "audit_mode": "durable_safety_failure" + }, + { + "case_id": "session_risk_hold", + "category": "session", + "preconditions": [ + "The approved risk policy places the session on hold." + ], + "expected_result": "denied", + "audit_mode": "durable_safety_failure" + }, + { + "case_id": "session_state_unknown", + "category": "integrity", + "preconditions": [ + "The stored session state is outside the approved registry." + ], + "expected_result": "unavailable", + "audit_mode": "durable_safety_failure" + }, + { + "case_id": "session_changed_during_evaluation", + "category": "race", + "preconditions": [ + "Session state changes before identity handoff completes." + ], + "expected_result": "unavailable", + "audit_mode": "durable_safety_failure" + }, + { + "case_id": "identity_link_missing", + "category": "identity_link", + "preconditions": ["No governed self identity link is available."], + "expected_result": "unavailable", + "audit_mode": "durable_safety_failure" + }, + { + "case_id": "identity_link_pending", + "category": "identity_link", + "preconditions": [ + "The governed self identity link remains pending." + ], + "expected_result": "unavailable", + "audit_mode": "durable_safety_failure" + }, + { + "case_id": "identity_link_ambiguous", + "category": "identity_link", + "preconditions": ["The governed identity linkage is ambiguous."], + "expected_result": "unavailable", + "audit_mode": "durable_safety_failure" + }, + { + "case_id": "identity_link_merged", + "category": "identity_link", + "preconditions": [ + "The evaluated identity link has been merged into another link." + ], + "expected_result": "unavailable", + "audit_mode": "durable_safety_failure" + }, + { + "case_id": "identity_link_revoked", + "category": "identity_link", + "preconditions": ["The evaluated identity link has been revoked."], + "expected_result": "unavailable", + "audit_mode": "durable_safety_failure" + }, + { + "case_id": "identity_link_verification_time_missing", + "category": "identity_link", + "preconditions": ["A verified link has no verification time."], + "expected_result": "unavailable", + "audit_mode": "durable_safety_failure" + }, + { + "case_id": "identity_link_wrong_principal", + "category": "identity_link", + "preconditions": [ + "The identity link is owned by a different principal." + ], + "expected_result": "unavailable", + "audit_mode": "durable_safety_failure" + }, + { + "case_id": "identity_link_duplicate_active", + "category": "identity_link", + "preconditions": [ + "More than one current identity link resolves for the same evaluation." + ], + "expected_result": "unavailable", + "audit_mode": "durable_safety_failure" + }, + { + "case_id": "identity_link_assurance_mismatch", + "category": "identity_link", + "preconditions": [ + "Identity-link assurance does not satisfy the approved policy." + ], + "expected_result": "unavailable", + "audit_mode": "durable_safety_failure" + }, + { + "case_id": "recovery_requested", + "category": "recovery", + "preconditions": [ + "A recovery transition has started and no replacement identity is trusted." + ], + "expected_result": "denied", + "audit_mode": "durable_evaluation" + }, + { + "case_id": "recovery_evidence_incomplete", + "category": "recovery", + "preconditions": ["Independent recovery proof is incomplete."], + "expected_result": "denied", + "audit_mode": "best_effort_denial" + }, + { + "case_id": "recovery_old_binding_not_cleared", + "category": "recovery", + "preconditions": [ + "The old local binding remains accessible during replacement." + ], + "expected_result": "unavailable", + "audit_mode": "durable_safety_failure" + }, + { + "case_id": "recovery_superseded_session_active", + "category": "recovery", + "preconditions": [ + "A session superseded by recovery remains active." + ], + "expected_result": "unavailable", + "audit_mode": "durable_safety_failure" + }, + { + "case_id": "recovery_fresh_proof_complete", + "category": "recovery", + "preconditions": [ + "Fresh approved proof succeeds, old binding is cleared, superseded sessions are revoked, and durable audit commits." + ], + "expected_result": "verified_self", + "audit_mode": "durable_evaluation" + }, + { + "case_id": "legal_representative_held", + "category": "representative", + "preconditions": [ + "A legal-representative relationship is presented while all representatives are held." + ], + "expected_result": "denied", + "audit_mode": "best_effort_denial" + }, + { + "case_id": "guardian_held", + "category": "representative", + "preconditions": [ + "A guardian relationship is presented while all representatives are held." + ], + "expected_result": "denied", + "audit_mode": "best_effort_denial" + }, + { + "case_id": "caregiver_held", + "category": "representative", + "preconditions": [ + "A caregiver relationship is presented while all representatives are held." + ], + "expected_result": "denied", + "audit_mode": "best_effort_denial" + }, + { + "case_id": "proxy_held", + "category": "representative", + "preconditions": [ + "A proxy relationship is presented while all representatives are held." + ], + "expected_result": "denied", + "audit_mode": "best_effort_denial" + }, + { + "case_id": "other_relationship_held", + "category": "representative", + "preconditions": [ + "An unapproved relationship is presented while all representatives are held." + ], + "expected_result": "denied", + "audit_mode": "best_effort_denial" + }, + { + "case_id": "evaluation_audit_unavailable", + "category": "audit", + "preconditions": [ + "Identity would otherwise be positive but durable evaluation audit cannot commit." + ], + "expected_result": "unavailable", + "audit_mode": "best_effort_unavailable" + }, + { + "case_id": "correlation_context_malformed", + "category": "integrity", + "preconditions": [ + "The request correlation context is malformed or untrusted." + ], + "expected_result": "unavailable", + "audit_mode": "durable_safety_failure" + }, + { + "case_id": "session_limit_policy_unresolved", + "category": "policy", + "preconditions": [ + "The applicable session-limit policy cannot be resolved." + ], + "expected_result": "unavailable", + "audit_mode": "durable_safety_failure" + }, + { + "case_id": "clock_untrusted", + "category": "dependency", + "preconditions": [ + "The trusted time needed for expiry evaluation is unavailable." + ], + "expected_result": "unavailable", + "audit_mode": "best_effort_unavailable" + }, + { + "case_id": "policy_version_mismatch", + "category": "policy", + "preconditions": [ + "Identity evidence and the active policy version do not match." + ], + "expected_result": "unavailable", + "audit_mode": "durable_safety_failure" + }, + { + "case_id": "identity_changed_before_handoff", + "category": "race", + "preconditions": [ + "Principal, session, or identity-link facts change before governed authorization handoff." + ], + "expected_result": "unavailable", + "audit_mode": "durable_safety_failure" + } + ] +} diff --git a/docs/nightingale/migration/candidates/v0/communication-notification-source-classification.json b/docs/nightingale/migration/candidates/v0/communication-notification-source-classification.json new file mode 100644 index 00000000..da60f0fa --- /dev/null +++ b/docs/nightingale/migration/candidates/v0/communication-notification-source-classification.json @@ -0,0 +1,1698 @@ +{ + "schema_version": 1, + "classification_id": "nightingale-communication-notification-source-classification.v1", + "reviewed_source_commit": "b6ea087747d7ea88c8a076f06f4c91a2636ea029", + "reviewed_at": "2026-07-27", + "source_revalidation": { + "revalidated_commit": "a979e78630c214f6f77d96ce702227da6c03a9b6", + "revalidated_at": "2026-07-27", + "changed_sources": [ + { + "path": "routes/api.php", + "previous_sha256": "052d9afd466b2646385986297e9ce3ec6a886d299b35c76067f5a0f21d7fa082", + "current_sha256": "a357886ae0975b63d978b975e53518a97d3f6e907820255a0f1c9e1b76a986ad", + "additions": 15, + "deletions": 5, + "classification_impact": "none", + "rationale": "The upstream delta adds a staff-only, independently gated Flow 4D conformance scene read and governed exception-note proposal beside the existing staff Arena conformance read. It adds no Nightingale route, patient communication operation, notification provider, patient-native transport, delivery-state claim, or implementation permission, so routes/api.php remains rejected migration evidence." + } + ] + }, + "status": "evidence_only_not_approved_for_implementation", + "scope": [ + "patient_communication_contract", + "patient_mutation_delivery", + "staff_handoff_routing", + "notification_registration_delivery", + "native_patient_experience", + "error_offline_urgency" + ], + "global_constraints": { + "implementation_permitted": false, + "runtime_adoption_permitted": false, + "route_registration_permitted": false, + "legacy_route_alias_permitted": false, + "legacy_topic_copy_permitted": false, + "legacy_urgent_copy_permitted": false, + "notification_provider_enabled": false, + "notification_device_registration_enabled": false, + "patient_push_delivery_enabled": false, + "patient_email_delivery_enabled": false, + "patient_sms_delivery_enabled": false, + "notification_payload_permitted": false, + "patient_foreground_polling_enabled": false, + "offline_mutation_queue_enabled": false, + "retry_identity_regeneration_permitted": false, + "server_acceptance_counts_as_care_team_delivery": false, + "production_data_used": false, + "production_query_permitted": false, + "production_replay_permitted": false, + "patient_or_principal_created": false + }, + "required_findings": { + "patient_push_delivery_absent": true, + "patient_native_automatic_refresh_absent": true, + "ambiguous_retry_reuses_operation_identity": false, + "server_acceptance_proves_staff_projection": false, + "ios_accepts_backend_escalated_delivery_state": false, + "android_maps_all_backend_ownership_states": false, + "android_maps_all_backend_delivery_states": false, + "urgent_guidance_is_locale_bound_release_content": false, + "notification_preferences_match_available_delivery_channels": false, + "staff_close_reason_breaks_patient_thread_decode": false + }, + "dispositions": { + "reimplement_principle_only": "A bounded safety property may inform a new Nightingale-owned design, but no code, identifier, route, schema, copy, state, provider, credential, or product policy is approved for reuse.", + "evidence_only": "The source or test is retained as evidence about current behavior and coverage. It is not a Nightingale requirement and cannot authorize implementation.", + "held": "The behavior depends on unresolved ownership, assurance, clinical, privacy, source, contract, content, accessibility, notification, or patient-language decisions.", + "reject": "The behavior is incompatible with the independent Nightingale boundary and must not be migrated." + }, + "decisions": { + "contract_evidence_only": { + "disposition": "evidence_only", + "rationale": "Inventory legacy operations, states, copy, provider assumptions, and hazards only; no path, operation identifier, schema, topic, notification copy, or security scheme becomes Nightingale authority." + }, + "configuration_rejected": { + "disposition": "reject", + "rationale": "Reject Hummingbird flags, policy/version values, topic copy, urgent guidance, response-window copy, provider settings, token names, and environment activation as Nightingale defaults." + }, + "routes_rejected": { + "disposition": "reject", + "rationale": "Reject legacy patient and staff routes, middleware compositions, schedules, aliases, redirects, and proxies; Nightingale remains a separate zero-operation contract." + }, + "ledger_principles_held": { + "disposition": "held", + "rationale": "Append-only events, encrypted content, content-free routing, idempotency, optimistic concurrency, and immutable delivery attempts are candidate properties, but every Nightingale table, state, retention rule, key lifecycle, and projection remains unapproved." + }, + "staff_dispatch_evidence_only": { + "disposition": "evidence_only", + "rationale": "Consumer heartbeat, bounded dispatch, reconciliation, and escalation scheduling are operational evidence only; no legacy command, queue, cadence, consumer, or worker is approved." + }, + "controller_boundary_evidence_only": { + "disposition": "evidence_only", + "rationale": "Request-time authorization, generic non-disclosure, no-store response handling, and audit boundaries are evidence; the controllers and their response semantics are not reusable Nightingale endpoints." + }, + "default_off_gates_principle_only": { + "disposition": "reimplement_principle_only", + "rationale": "Layered default-off product, operation, policy, source, and staff-readiness gates are required in principle; Hummingbird middleware and configuration are rejected." + }, + "request_shapes_held": { + "disposition": "held", + "rationale": "Bounded text, control-character rejection, explicit concurrency, and correction/retraction separation are candidate controls; patient topics, body limits, close reasons, urgent versioning, and mutation shapes require Nightingale approval." + }, + "notification_registration_held": { + "disposition": "held", + "rationale": "Token encryption, digest lookup, principal ownership, rebind/revoke transactions, and audit are candidate controls, but registration alone is not delivery and no Nightingale provider, payload, consent, lifecycle, or native registration exists." + }, + "staff_request_shapes_held": { + "disposition": "held", + "rationale": "Claim, response, close, release, reassignment, reroute, version, and idempotency inputs remain staff-workflow evidence; Nightingale needs independently approved patient-to-team responsibility semantics." + }, + "patient_ledgers_principle_only": { + "disposition": "reimplement_principle_only", + "rationale": "Immutable encrypted message content, append-only receipts/routing, content-free outbox, and explicit device/delivery records are useful principles; legacy models, states, identifiers, retention, and access relationships are not approved." + }, + "staff_projection_held": { + "disposition": "held", + "rationale": "Responsibility pools, eligible responders, fresh heartbeats, work-item projection, actions, and round-question promotion are useful evidence but require clinical operations ownership, audit, downtime, and staffing approval." + }, + "messaging_policy_principle_only": { + "disposition": "reimplement_principle_only", + "rationale": "Reimplement exact current encounter/grant authorization, immutable encrypted content, append-only corrections, content-free routing, and replay safety only after a Nightingale contract and named approvals exist." + }, + "staff_handoff_principle_only": { + "disposition": "reimplement_principle_only", + "rationale": "Reimplement verified routing readiness, responsibility ownership, bounded escalation, lifecycle reconciliation, and patient-visible receipts as independently specified Nightingale behavior; legacy states and services are evidence only." + }, + "test_evidence_only": { + "disposition": "evidence_only", + "rationale": "Retain test cases as hazard, privacy, authorization, replay, routing, and rendering evidence; no fixture, assertion vocabulary, test route, synthetic identity, or expected copy authorizes product behavior." + }, + "patient_client_held": { + "disposition": "held", + "rationale": "The legacy patient clients expose useful failure and accessibility lessons but are not migrated: their retry identity, delivery wording, enum handling, refresh behavior, notification preferences, copy, and offline behavior require new Nightingale decisions." + }, + "staff_client_evidence_only": { + "disposition": "evidence_only", + "rationale": "Foreground polling, privacy-aware mutation handling, routing controls, and staff state rendering are compatibility evidence only; Nightingale must not copy staff Hummingbird code or operational identity." + } + }, + "sources": [ + { + "path": "app/Console/Commands/HummingbirdConsumePatientMessageHandoffCommand.php", + "sha256": "3da169d2cbd999e7984c89c12158db852b4b16d5a46143d43b8c467724104797", + "surface": "legacy_backend", + "categories": [ + "patient_mutation_delivery", + "staff_handoff_routing", + "error_offline_urgency" + ], + "decision_id": "staff_dispatch_evidence_only" + }, + { + "path": "app/Console/Commands/HummingbirdEscalatePatientCommunicationsCommand.php", + "sha256": "e724fe987d12767781afcd8c66444ed9fb203f1d00e8100c3c1544ecd33c6d17", + "surface": "legacy_backend", + "categories": [ + "patient_mutation_delivery", + "staff_handoff_routing", + "error_offline_urgency" + ], + "decision_id": "staff_dispatch_evidence_only" + }, + { + "path": "app/Console/Commands/HummingbirdReconcilePatientCommunicationsCommand.php", + "sha256": "a6c46201878b7bfac3e9b68cda066584791fdb9ee46971ef6e067d4e8a25d0b8", + "surface": "legacy_backend", + "categories": [ + "patient_mutation_delivery", + "staff_handoff_routing", + "error_offline_urgency" + ], + "decision_id": "staff_dispatch_evidence_only" + }, + { + "path": "app/Contracts/Patient/PatientMessageHandoffReadiness.php", + "sha256": "247a5268c8cd037b1cad90b210492aaf723a6723e275d4a268fd550bc00db25c", + "surface": "legacy_backend", + "categories": [ + "patient_mutation_delivery", + "staff_handoff_routing", + "error_offline_urgency" + ], + "decision_id": "staff_dispatch_evidence_only" + }, + { + "path": "app/Http/Controllers/Api/Mobile/PatientCommunicationController.php", + "sha256": "42c33a3fb4783868d62c02c42eef03ca3b0dee9d5650a8c3998ebb9fcdb0b334", + "surface": "legacy_backend", + "categories": [ + "patient_communication_contract", + "patient_mutation_delivery", + "staff_handoff_routing", + "notification_registration_delivery", + "error_offline_urgency" + ], + "decision_id": "controller_boundary_evidence_only" + }, + { + "path": "app/Http/Controllers/Api/Patient/EducationClarificationController.php", + "sha256": "97a2833c561b9aa963afb82707f1f9ce5c5f8e696b493568a86436bd50bebfb1", + "surface": "legacy_backend", + "categories": [ + "patient_communication_contract", + "patient_mutation_delivery", + "staff_handoff_routing", + "notification_registration_delivery", + "error_offline_urgency" + ], + "decision_id": "controller_boundary_evidence_only" + }, + { + "path": "app/Http/Controllers/Api/Patient/MessagingController.php", + "sha256": "2e2f29809b549f5ee5fbd095557793b261eb69451ad361c2a4690dc561b23f79", + "surface": "legacy_backend", + "categories": [ + "patient_communication_contract", + "patient_mutation_delivery", + "staff_handoff_routing", + "notification_registration_delivery", + "error_offline_urgency" + ], + "decision_id": "controller_boundary_evidence_only" + }, + { + "path": "app/Http/Controllers/Api/Patient/NotificationDeviceController.php", + "sha256": "56c2271d0433c9c4549f3c791619938f77e65156c1ae3413b8997c740f53b6c9", + "surface": "legacy_backend", + "categories": [ + "patient_communication_contract", + "patient_mutation_delivery", + "staff_handoff_routing", + "notification_registration_delivery", + "error_offline_urgency" + ], + "decision_id": "controller_boundary_evidence_only" + }, + { + "path": "app/Http/Controllers/PatientCommunicationController.php", + "sha256": "a050854a23b785647abd791d046c9601b456460f03e32c7a340d17c041b7ff32", + "surface": "legacy_backend", + "categories": [ + "patient_communication_contract", + "patient_mutation_delivery", + "staff_handoff_routing", + "notification_registration_delivery", + "error_offline_urgency" + ], + "decision_id": "controller_boundary_evidence_only" + }, + { + "path": "app/Http/Middleware/EnsureHummingbirdPatientEnabled.php", + "sha256": "0fa5ff4f732b171873450e66f377fd03b51ae5231a7b21464160e999d82b6b9c", + "surface": "legacy_backend", + "categories": [ + "patient_communication_contract", + "patient_mutation_delivery", + "staff_handoff_routing", + "notification_registration_delivery", + "error_offline_urgency" + ], + "decision_id": "default_off_gates_principle_only" + }, + { + "path": "app/Http/Middleware/EnsureHummingbirdPatientFeatureEnabled.php", + "sha256": "087dca7053d8199974589fd81fe88e1e03ff8da54f0ce6ac6305b7014f422b07", + "surface": "legacy_backend", + "categories": [ + "patient_communication_contract", + "patient_mutation_delivery", + "staff_handoff_routing", + "notification_registration_delivery", + "error_offline_urgency" + ], + "decision_id": "default_off_gates_principle_only" + }, + { + "path": "app/Http/Middleware/EnsurePatientStaffMessagingEnabled.php", + "sha256": "9d4420eafd60522768b1d04fee818bdca32c38748e5fe7a4f015798ec7e5f960", + "surface": "legacy_backend", + "categories": [ + "patient_communication_contract", + "patient_mutation_delivery", + "staff_handoff_routing", + "notification_registration_delivery", + "error_offline_urgency" + ], + "decision_id": "default_off_gates_principle_only" + }, + { + "path": "app/Http/Middleware/ProtectPatientCommunicationResponse.php", + "sha256": "885dddde03d5617530d6fffc46f8a25c1d4bb0a73866c19d00241f151002f167", + "surface": "legacy_backend", + "categories": [ + "patient_communication_contract", + "patient_mutation_delivery", + "staff_handoff_routing", + "notification_registration_delivery", + "error_offline_urgency" + ], + "decision_id": "default_off_gates_principle_only" + }, + { + "path": "app/Http/Requests/Patient/AmendPatientMessageRequest.php", + "sha256": "2fa07af6a2ab59417de929a603547e865034581e2480c578e9a47abea3b161b4", + "surface": "legacy_backend", + "categories": [ + "patient_communication_contract", + "patient_mutation_delivery", + "error_offline_urgency" + ], + "decision_id": "request_shapes_held" + }, + { + "path": "app/Http/Requests/Patient/CloseMessageThreadRequest.php", + "sha256": "fe6ec2a51dbcc8361e8fc5c4c09f30060b04b2996b3ba2c94864a89578a72c78", + "surface": "legacy_backend", + "categories": [ + "patient_communication_contract", + "patient_mutation_delivery", + "error_offline_urgency" + ], + "decision_id": "request_shapes_held" + }, + { + "path": "app/Http/Requests/Patient/CreateEducationClarificationRequest.php", + "sha256": "ae9b162f2e2208a9f532b8ce283c13219f87a5a938d5b614e674bec24efbd12f", + "surface": "legacy_backend", + "categories": [ + "patient_communication_contract", + "patient_mutation_delivery", + "error_offline_urgency" + ], + "decision_id": "request_shapes_held" + }, + { + "path": "app/Http/Requests/Patient/CreateMessageThreadRequest.php", + "sha256": "3d6bb0dbecbf3a5f31a79522fc5c86ce68c25f49935d059dc02bf8897173bb7b", + "surface": "legacy_backend", + "categories": [ + "patient_communication_contract", + "patient_mutation_delivery", + "error_offline_urgency" + ], + "decision_id": "request_shapes_held" + }, + { + "path": "app/Http/Requests/Patient/RegisterPatientNotificationDeviceRequest.php", + "sha256": "f62e541cbe4177fb70ef771931fca14780d4ca1b636321cdbb2972673df53b86", + "surface": "legacy_backend", + "categories": [ + "notification_registration_delivery", + "error_offline_urgency" + ], + "decision_id": "notification_registration_held" + }, + { + "path": "app/Http/Requests/Patient/SendPatientMessageRequest.php", + "sha256": "382b0eae026265092fc4ca16ccf66a709c77afa2887ff1e33101246a270ef381", + "surface": "legacy_backend", + "categories": [ + "patient_communication_contract", + "patient_mutation_delivery", + "error_offline_urgency" + ], + "decision_id": "request_shapes_held" + }, + { + "path": "app/Http/Requests/PatientCommunication/ClaimPatientCommunicationRequest.php", + "sha256": "db6347e8e32a4cb24c0b174ae2fd49ec6372013462f328c9f0a5569c416c04c3", + "surface": "legacy_backend", + "categories": [ + "patient_mutation_delivery", + "staff_handoff_routing", + "error_offline_urgency" + ], + "decision_id": "staff_request_shapes_held" + }, + { + "path": "app/Http/Requests/PatientCommunication/ClosePatientCommunicationRequest.php", + "sha256": "15da3ae49a6cafb68d0df3ee7334a60a2c76d7a0b476b473950b482f3a58287e", + "surface": "legacy_backend", + "categories": [ + "patient_mutation_delivery", + "staff_handoff_routing", + "error_offline_urgency" + ], + "decision_id": "staff_request_shapes_held" + }, + { + "path": "app/Http/Requests/PatientCommunication/ReassignPatientCommunicationRequest.php", + "sha256": "0ea79011e5fd0228d5cd95f11b04937a097879f3205b4fedaab9fea0eaa25ec7", + "surface": "legacy_backend", + "categories": [ + "patient_mutation_delivery", + "staff_handoff_routing", + "error_offline_urgency" + ], + "decision_id": "staff_request_shapes_held" + }, + { + "path": "app/Http/Requests/PatientCommunication/ReleasePatientCommunicationRequest.php", + "sha256": "86818ebb9b3a714c3bf7096f6e0a6bfc998dae4b609344f074abe2b31eacf5e5", + "surface": "legacy_backend", + "categories": [ + "patient_mutation_delivery", + "staff_handoff_routing", + "error_offline_urgency" + ], + "decision_id": "staff_request_shapes_held" + }, + { + "path": "app/Http/Requests/PatientCommunication/ReplyPatientCommunicationRequest.php", + "sha256": "fcd774d616e427c2bfaafe9ac0a29c580a836334ce4b37103eb6ca9a295acb2e", + "surface": "legacy_backend", + "categories": [ + "patient_mutation_delivery", + "staff_handoff_routing", + "error_offline_urgency" + ], + "decision_id": "staff_request_shapes_held" + }, + { + "path": "app/Http/Requests/PatientCommunication/ReroutePatientCommunicationRequest.php", + "sha256": "17fe54dd5d0e288f415631ff7686ddaff955c2c4c251479cda19e69cc670878a", + "surface": "legacy_backend", + "categories": [ + "patient_mutation_delivery", + "staff_handoff_routing", + "error_offline_urgency" + ], + "decision_id": "staff_request_shapes_held" + }, + { + "path": "app/Http/Requests/PatientCommunication/StaffMessageMutationRequest.php", + "sha256": "d3bcfe1cd4db6f3782cc9d39a428a9ae9af7587c2a52b64a3b7550088ae1c8de", + "surface": "legacy_backend", + "categories": [ + "patient_mutation_delivery", + "staff_handoff_routing", + "error_offline_urgency" + ], + "decision_id": "staff_request_shapes_held" + }, + { + "path": "app/Models/Patient/PatientEducationClarificationRequest.php", + "sha256": "2ee339f245abb261431b9b11b8ec9206ada5bc1392dedca5aced93931a314f4e", + "surface": "legacy_backend", + "categories": [ + "patient_communication_contract", + "patient_mutation_delivery", + "staff_handoff_routing", + "notification_registration_delivery", + "error_offline_urgency" + ], + "decision_id": "patient_ledgers_principle_only" + }, + { + "path": "app/Models/Patient/PatientMessage.php", + "sha256": "300d4d3a7c93326276c5fd6160b1382a4a16ca019f48b8401f6cba3908b079af", + "surface": "legacy_backend", + "categories": [ + "patient_communication_contract", + "patient_mutation_delivery", + "staff_handoff_routing", + "notification_registration_delivery", + "error_offline_urgency" + ], + "decision_id": "patient_ledgers_principle_only" + }, + { + "path": "app/Models/Patient/PatientMessageDeliveryReceipt.php", + "sha256": "1d2c2f68385e7dd93eaf13df4a6673d09a733289ffeeb8c249f46a8219b6cd25", + "surface": "legacy_backend", + "categories": [ + "patient_communication_contract", + "patient_mutation_delivery", + "staff_handoff_routing", + "notification_registration_delivery", + "error_offline_urgency" + ], + "decision_id": "patient_ledgers_principle_only" + }, + { + "path": "app/Models/Patient/PatientMessageRoutingEvent.php", + "sha256": "d8d822f1dcfb4b813ec1f3a58f25000cdbfcc5835d9a383ae1756c1b5db685b3", + "surface": "legacy_backend", + "categories": [ + "patient_communication_contract", + "patient_mutation_delivery", + "staff_handoff_routing", + "notification_registration_delivery", + "error_offline_urgency" + ], + "decision_id": "patient_ledgers_principle_only" + }, + { + "path": "app/Models/Patient/PatientMessageThread.php", + "sha256": "81dad45c8ffc4acdf9ad8a3669fb87f937ccd3a80529777d263698271a6e6848", + "surface": "legacy_backend", + "categories": [ + "patient_communication_contract", + "patient_mutation_delivery", + "staff_handoff_routing", + "notification_registration_delivery", + "error_offline_urgency" + ], + "decision_id": "patient_ledgers_principle_only" + }, + { + "path": "app/Models/Patient/PatientNotificationDeliveryAttempt.php", + "sha256": "f9b548099c7bd82f2c426c765b8b0d52c402ab28063621ba438c61cb37482e48", + "surface": "legacy_backend", + "categories": [ + "patient_communication_contract", + "patient_mutation_delivery", + "staff_handoff_routing", + "notification_registration_delivery", + "error_offline_urgency" + ], + "decision_id": "patient_ledgers_principle_only" + }, + { + "path": "app/Models/Patient/PatientNotificationDevice.php", + "sha256": "d07ed0f4830387121c30a3c97788553e2ee1b83c478346ec348eda425f74dc92", + "surface": "legacy_backend", + "categories": [ + "patient_communication_contract", + "patient_mutation_delivery", + "staff_handoff_routing", + "notification_registration_delivery", + "error_offline_urgency" + ], + "decision_id": "patient_ledgers_principle_only" + }, + { + "path": "app/Models/Patient/PatientNotificationOutbox.php", + "sha256": "49eb137476e21451cc117a872fbe84ffc2208cd693f325a885a8003ed90587b8", + "surface": "legacy_backend", + "categories": [ + "patient_communication_contract", + "patient_mutation_delivery", + "staff_handoff_routing", + "notification_registration_delivery", + "error_offline_urgency" + ], + "decision_id": "patient_ledgers_principle_only" + }, + { + "path": "app/Models/PatientCommunication/ConsumerHeartbeat.php", + "sha256": "5bebca93ccf77d5a5474a779a6dd165c8bdaec39811e2dccc3ed022b81a7cf07", + "surface": "legacy_backend", + "categories": [ + "patient_mutation_delivery", + "staff_handoff_routing", + "error_offline_urgency" + ], + "decision_id": "staff_projection_held" + }, + { + "path": "app/Models/PatientCommunication/PoolMembership.php", + "sha256": "4710c13e82ee4061e2b7982a997d014bc7146a8f4483676a0f3022ec31be5a98", + "surface": "legacy_backend", + "categories": [ + "patient_mutation_delivery", + "staff_handoff_routing", + "error_offline_urgency" + ], + "decision_id": "staff_projection_held" + }, + { + "path": "app/Models/PatientCommunication/ResponsibilityPool.php", + "sha256": "43913a036643a1bd5661b60b67e1f7b1f9fec3553afae067de5036d3e54844dc", + "surface": "legacy_backend", + "categories": [ + "patient_mutation_delivery", + "staff_handoff_routing", + "error_offline_urgency" + ], + "decision_id": "staff_projection_held" + }, + { + "path": "app/Models/PatientCommunication/RoundQuestionPromotion.php", + "sha256": "2bff340ff8d72337549b7673e6ba6cc4b0908945d96bbf71101fb95889ebb4f4", + "surface": "legacy_backend", + "categories": [ + "patient_mutation_delivery", + "staff_handoff_routing", + "error_offline_urgency" + ], + "decision_id": "staff_projection_held" + }, + { + "path": "app/Models/PatientCommunication/RoundQuestionPromotionOutcome.php", + "sha256": "4a5972591afb71983d044f96a9140558ef69618e143a7215392e0bf29aae669a", + "surface": "legacy_backend", + "categories": [ + "patient_mutation_delivery", + "staff_handoff_routing", + "error_offline_urgency" + ], + "decision_id": "staff_projection_held" + }, + { + "path": "app/Models/PatientCommunication/StaffActionEvent.php", + "sha256": "dcc99b8ebb9d6f4f7f59d6e003d5edc4909303f2f681efa04cb24a710d501ba5", + "surface": "legacy_backend", + "categories": [ + "patient_mutation_delivery", + "staff_handoff_routing", + "error_offline_urgency" + ], + "decision_id": "staff_projection_held" + }, + { + "path": "app/Models/PatientCommunication/ThreadWorkItem.php", + "sha256": "7005189c143ef68965581e27d3f89d5be772b3f39ce12ea01c4b2542aec1714f", + "surface": "legacy_backend", + "categories": [ + "patient_mutation_delivery", + "staff_handoff_routing", + "error_offline_urgency" + ], + "decision_id": "staff_projection_held" + }, + { + "path": "app/Policies/Patient/PatientMessageThreadPolicy.php", + "sha256": "1f0271f1fd4a8553c2b2e16229c0fc425d41eb6a6466e600b7fa571af8242bd8", + "surface": "legacy_backend", + "categories": [ + "patient_communication_contract", + "patient_mutation_delivery", + "error_offline_urgency" + ], + "decision_id": "messaging_policy_principle_only" + }, + { + "path": "app/Services/Patient/Education/PatientEducationClarificationService.php", + "sha256": "fb1e091aecebb0c4f89cf7c32e1a8375f681b704c41054d3710fa0ddcc24faae", + "surface": "legacy_backend", + "categories": [ + "patient_communication_contract", + "patient_mutation_delivery", + "error_offline_urgency" + ], + "decision_id": "messaging_policy_principle_only" + }, + { + "path": "app/Services/Patient/Messaging/DatabasePatientMessageHandoffConsumer.php", + "sha256": "a0c46d3c92aa39a5ed16ff9b9d1f04a4e82329649a745f1a86b7854068c67f4f", + "surface": "legacy_backend", + "categories": [ + "patient_mutation_delivery", + "staff_handoff_routing", + "error_offline_urgency" + ], + "decision_id": "staff_handoff_principle_only" + }, + { + "path": "app/Services/Patient/Messaging/PatientCommunicationEncounterGuard.php", + "sha256": "8a65438b3779252e88c35ee1dd91cc40e0d0d74e8018f5ac1a1d7a210a617976", + "surface": "legacy_backend", + "categories": [ + "patient_communication_contract", + "patient_mutation_delivery", + "error_offline_urgency" + ], + "decision_id": "messaging_policy_principle_only" + }, + { + "path": "app/Services/Patient/Messaging/PatientCommunicationEscalationService.php", + "sha256": "265ca6a7cd390d75b605f2cc6195eb0824d7cf45454c90f77505bd25f0d8c876", + "surface": "legacy_backend", + "categories": [ + "patient_mutation_delivery", + "staff_handoff_routing", + "error_offline_urgency" + ], + "decision_id": "staff_handoff_principle_only" + }, + { + "path": "app/Services/Patient/Messaging/PatientCommunicationLifecycleReconciliationService.php", + "sha256": "2676d63146f7c88ef8a53fc75b4f26abc01a879795edd328e3b3544034787347", + "surface": "legacy_backend", + "categories": [ + "patient_mutation_delivery", + "staff_handoff_routing", + "error_offline_urgency" + ], + "decision_id": "staff_handoff_principle_only" + }, + { + "path": "app/Services/Patient/Messaging/PatientCommunicationPoolResolver.php", + "sha256": "696ebf72279c21ef718e63b4e8c7ac4049e6dc60ed3795fa6da939a104816e8c", + "surface": "legacy_backend", + "categories": [ + "patient_mutation_delivery", + "staff_handoff_routing", + "error_offline_urgency" + ], + "decision_id": "staff_handoff_principle_only" + }, + { + "path": "app/Services/Patient/Messaging/PatientCommunicationResponderEligibility.php", + "sha256": "bdfca2bcb190f6647aef4045926e005826860b5c790cfe94eff439412c70ba20", + "surface": "legacy_backend", + "categories": [ + "patient_mutation_delivery", + "staff_handoff_routing", + "error_offline_urgency" + ], + "decision_id": "staff_handoff_principle_only" + }, + { + "path": "app/Services/Patient/Messaging/PatientMessageCipher.php", + "sha256": "a762991c7f05a4432e1532671d780a74d18da3faf437bc40ee7afb1cdd380ab1", + "surface": "legacy_backend", + "categories": [ + "patient_communication_contract", + "patient_mutation_delivery", + "error_offline_urgency" + ], + "decision_id": "messaging_policy_principle_only" + }, + { + "path": "app/Services/Patient/Messaging/PatientMessagingFailure.php", + "sha256": "60b274d0f944bd8882f1ad84b3a5d2194814814ebe235a89a17d8a827c26aac9", + "surface": "legacy_backend", + "categories": [ + "patient_communication_contract", + "patient_mutation_delivery", + "error_offline_urgency" + ], + "decision_id": "messaging_policy_principle_only" + }, + { + "path": "app/Services/Patient/Messaging/PatientMessagingPolicyRegistry.php", + "sha256": "44deadecf94ad6321d2da3adc712545fea5667d11ffc0ba3bb9efe54002ae2c8", + "surface": "legacy_backend", + "categories": [ + "patient_communication_contract", + "patient_mutation_delivery", + "error_offline_urgency" + ], + "decision_id": "messaging_policy_principle_only" + }, + { + "path": "app/Services/Patient/Messaging/PatientMessagingService.php", + "sha256": "a8cbe9aa98344fcd136a442fc8a41d9f1b5d18d632c927194980107e1183e0bf", + "surface": "legacy_backend", + "categories": [ + "patient_communication_contract", + "patient_mutation_delivery", + "error_offline_urgency" + ], + "decision_id": "messaging_policy_principle_only" + }, + { + "path": "app/Services/Patient/Messaging/StaffPatientCommunicationFailure.php", + "sha256": "3f3e217dac9cb7a8ccc258e8f32c7cb9a9a9d3a9f6e942963f88cdb00c5f99da", + "surface": "legacy_backend", + "categories": [ + "patient_mutation_delivery", + "staff_handoff_routing", + "error_offline_urgency" + ], + "decision_id": "staff_handoff_principle_only" + }, + { + "path": "app/Services/Patient/Messaging/StaffPatientCommunicationRoutingPolicy.php", + "sha256": "bb2dfbea6b8df56f21608f4e3af1446ad499de19c5fb445570c51e9b2e69c8bc", + "surface": "legacy_backend", + "categories": [ + "patient_mutation_delivery", + "staff_handoff_routing", + "error_offline_urgency" + ], + "decision_id": "staff_handoff_principle_only" + }, + { + "path": "app/Services/Patient/Messaging/StaffPatientCommunicationService.php", + "sha256": "219ccf3f609404241dcaf33425eeb81c895458af7d1ee4f2a4212fea17bd8905", + "surface": "legacy_backend", + "categories": [ + "patient_mutation_delivery", + "staff_handoff_routing", + "error_offline_urgency" + ], + "decision_id": "staff_handoff_principle_only" + }, + { + "path": "app/Services/Patient/PatientNotificationDeviceCipher.php", + "sha256": "084507c176924036bed42be5f0c795cada34b0ad932aa0b188422be2f1791042", + "surface": "legacy_backend", + "categories": [ + "notification_registration_delivery", + "error_offline_urgency" + ], + "decision_id": "notification_registration_held" + }, + { + "path": "app/Services/Patient/PatientNotificationDeviceFailure.php", + "sha256": "df9360add492ff5319bbea8a02b71a44c79881d4e71703798ab6ad5e1c19bca3", + "surface": "legacy_backend", + "categories": [ + "notification_registration_delivery", + "error_offline_urgency" + ], + "decision_id": "notification_registration_held" + }, + { + "path": "app/Services/Patient/PatientNotificationDeviceRegistry.php", + "sha256": "99c3209dd63340489448390139368b9d8d9dc5ee583907a6744ddc3a6bb87b3c", + "surface": "legacy_backend", + "categories": [ + "notification_registration_delivery", + "error_offline_urgency" + ], + "decision_id": "notification_registration_held" + }, + { + "path": "config/hummingbird-patient-content.php", + "sha256": "a4f23f9efba3e8364953cfa6bc2e45183ae8f054fdd49b012f684cd86e80aefa", + "surface": "legacy_backend", + "categories": [ + "patient_communication_contract", + "patient_mutation_delivery", + "staff_handoff_routing", + "notification_registration_delivery", + "error_offline_urgency" + ], + "decision_id": "configuration_rejected" + }, + { + "path": "config/hummingbird-patient.php", + "sha256": "d5dafe054077892e649fd44f95edc6bc1bb499e4dffeb4f4390316823db07625", + "surface": "legacy_backend", + "categories": [ + "patient_communication_contract", + "patient_mutation_delivery", + "staff_handoff_routing", + "notification_registration_delivery", + "error_offline_urgency" + ], + "decision_id": "configuration_rejected" + }, + { + "path": "database/migrations/2026_07_19_000300_create_patient_experience_messaging_foundation.php", + "sha256": "6236292c066ea566ff49f3b9e374b5f2cb592d0b00b322af0dfa95d4dd711cdb", + "surface": "legacy_database", + "categories": [ + "patient_communication_contract", + "patient_mutation_delivery", + "staff_handoff_routing", + "notification_registration_delivery", + "error_offline_urgency" + ], + "decision_id": "ledger_principles_held" + }, + { + "path": "database/migrations/2026_07_19_000400_create_patient_communications_staff_handoff_foundation.php", + "sha256": "3daa37f225734656c6b69462bf6ada4511bc2ed2239758d6b6ffc6049196bcd6", + "surface": "legacy_database", + "categories": [ + "patient_communication_contract", + "patient_mutation_delivery", + "staff_handoff_routing", + "notification_registration_delivery", + "error_offline_urgency" + ], + "decision_id": "ledger_principles_held" + }, + { + "path": "database/migrations/2026_07_21_000900_limit_patient_message_amendments.php", + "sha256": "7a5af7bc231b3997afc6b1c4d5b129bf617b5b5c000d7401c5fdbecfd81e1657", + "surface": "legacy_database", + "categories": [ + "patient_communication_contract", + "patient_mutation_delivery", + "staff_handoff_routing", + "notification_registration_delivery", + "error_offline_urgency" + ], + "decision_id": "ledger_principles_held" + }, + { + "path": "database/migrations/2026_07_21_001000_extend_patient_message_routing_events_for_amendments.php", + "sha256": "7486fca274724d86f63f3f70903d631713877123f05e4a9f26ec11a7b0de45b9", + "surface": "legacy_database", + "categories": [ + "patient_communication_contract", + "patient_mutation_delivery", + "staff_handoff_routing", + "notification_registration_delivery", + "error_offline_urgency" + ], + "decision_id": "ledger_principles_held" + }, + { + "path": "database/migrations/2026_07_22_001100_create_patient_notification_devices.php", + "sha256": "580455e8f54a0abdf25181a39a416d8c4f052dc5503c9ab24c5854683d591178", + "surface": "legacy_database", + "categories": [ + "patient_communication_contract", + "patient_mutation_delivery", + "staff_handoff_routing", + "notification_registration_delivery", + "error_offline_urgency" + ], + "decision_id": "ledger_principles_held" + }, + { + "path": "docs/hummingbird/api-contract/hummingbird-patient.v1.yaml", + "sha256": "fb6220b4ef8eb106223624a9785256fdc1603995f281f430a79897905cb45a1b", + "surface": "legacy_contract", + "categories": [ + "patient_communication_contract", + "patient_mutation_delivery", + "staff_handoff_routing", + "notification_registration_delivery", + "error_offline_urgency" + ], + "decision_id": "contract_evidence_only" + }, + { + "path": "docs/hummingbird/PUSH.md", + "sha256": "80fd3a832af621dfe37a2ea9ba23174a77901c404553ef5dc21d141eabe8c1ff", + "surface": "legacy_contract", + "categories": [ + "patient_communication_contract", + "patient_mutation_delivery", + "staff_handoff_routing", + "notification_registration_delivery", + "error_offline_urgency" + ], + "decision_id": "contract_evidence_only" + }, + { + "path": "docs/hummingbird/reference/05-notifications-earned-urgency.md", + "sha256": "7b8d94cf4353de385ab9b5614be58899798019e2f6f409a01b5e6cfa2247a3cc", + "surface": "legacy_contract", + "categories": [ + "patient_communication_contract", + "patient_mutation_delivery", + "staff_handoff_routing", + "notification_registration_delivery", + "error_offline_urgency" + ], + "decision_id": "contract_evidence_only" + }, + { + "path": "hummingbird/androidApp/app/src/androidTest/java/net/acumenus/hummingbird/ui/communications/PatientCommunicationsUiTest.kt", + "sha256": "f8859812fe8380f41b6238fe3d882557e8eac29de1490cb56f5b1ad43cfff6bc", + "surface": "legacy_staff_android", + "categories": [ + "staff_handoff_routing", + "native_patient_experience", + "error_offline_urgency" + ], + "decision_id": "staff_client_evidence_only" + }, + { + "path": "hummingbird/androidApp/app/src/main/java/net/acumenus/hummingbird/data/PatientCommunicationModels.kt", + "sha256": "593d9fcc4711f6145bf9b7818dd6aa32318fe2420574386e6c3646257d7bd8f9", + "surface": "legacy_staff_android", + "categories": [ + "staff_handoff_routing", + "native_patient_experience", + "error_offline_urgency" + ], + "decision_id": "staff_client_evidence_only" + }, + { + "path": "hummingbird/androidApp/app/src/main/java/net/acumenus/hummingbird/data/PatientCommunicationsViewModel.kt", + "sha256": "b48a86f01f7b652a541994fa01baddea65518474618ab097aabd2d6871f3c4dd", + "surface": "legacy_staff_android", + "categories": [ + "staff_handoff_routing", + "native_patient_experience", + "error_offline_urgency" + ], + "decision_id": "staff_client_evidence_only" + }, + { + "path": "hummingbird/androidApp/app/src/main/java/net/acumenus/hummingbird/ui/communications/PatientCommunicationsPolling.kt", + "sha256": "e2763d1a7e12e0d8889097d7cab9e93d5208a4210d0f1ba1b721b724f4fa9389", + "surface": "legacy_staff_android", + "categories": [ + "staff_handoff_routing", + "native_patient_experience", + "error_offline_urgency" + ], + "decision_id": "staff_client_evidence_only" + }, + { + "path": "hummingbird/androidApp/app/src/main/java/net/acumenus/hummingbird/ui/communications/PatientCommunicationsScreens.kt", + "sha256": "3d7e4109611bc075107b3560371067fb24a12a8c324205dfd67f919f8cb22cec", + "surface": "legacy_staff_android", + "categories": [ + "staff_handoff_routing", + "native_patient_experience", + "error_offline_urgency" + ], + "decision_id": "staff_client_evidence_only" + }, + { + "path": "hummingbird/androidApp/app/src/test/java/net/acumenus/hummingbird/data/PatientCommunicationsContractTest.kt", + "sha256": "9088fe05f01812f639fe4361ff29e331692c727e4e66bcece6b66c3e22e3ca32", + "surface": "legacy_staff_android", + "categories": [ + "staff_handoff_routing", + "native_patient_experience", + "error_offline_urgency" + ], + "decision_id": "staff_client_evidence_only" + }, + { + "path": "hummingbird/androidApp/app/src/test/java/net/acumenus/hummingbird/data/PatientCommunicationsViewModelPrivacyTest.kt", + "sha256": "90698bfe7734ff088c7aa8addc534ac5dca630a82194eb6f281cc035a1979b5b", + "surface": "legacy_staff_android", + "categories": [ + "staff_handoff_routing", + "native_patient_experience", + "error_offline_urgency" + ], + "decision_id": "staff_client_evidence_only" + }, + { + "path": "hummingbird/androidPatientApp/app/src/androidTest/java/net/acumenus/hummingbird/patient/PatientPrimaryJourneyInstrumentedTest.kt", + "sha256": "28c26e4de35089445d4080920c4ccda1ce5d3886597f9cb0b5287fef29484c17", + "surface": "legacy_android_test", + "categories": [ + "patient_communication_contract", + "patient_mutation_delivery", + "notification_registration_delivery", + "native_patient_experience", + "error_offline_urgency" + ], + "decision_id": "test_evidence_only" + }, + { + "path": "hummingbird/androidPatientApp/app/src/main/AndroidManifest.xml", + "sha256": "42cb31361a537365cf274dfbbb4b9c89e3c9481c01caa0a5bfe2665a27d6864d", + "surface": "legacy_android", + "categories": [ + "patient_communication_contract", + "patient_mutation_delivery", + "notification_registration_delivery", + "native_patient_experience", + "error_offline_urgency" + ], + "decision_id": "patient_client_held" + }, + { + "path": "hummingbird/androidPatientApp/app/src/main/java/net/acumenus/hummingbird/patient/data/PatientApiClient.kt", + "sha256": "9f803c7533f5bd7b2d2877777e8274b17a7fa0c8455395b4b3ddfe4ded26e3f5", + "surface": "legacy_android", + "categories": [ + "patient_communication_contract", + "patient_mutation_delivery", + "notification_registration_delivery", + "native_patient_experience", + "error_offline_urgency" + ], + "decision_id": "patient_client_held" + }, + { + "path": "hummingbird/androidPatientApp/app/src/main/java/net/acumenus/hummingbird/patient/data/PatientApiModels.kt", + "sha256": "a1932c3dc2f037f4006c72e083d57bc6219f63de42f348c7909737707d25e115", + "surface": "legacy_android", + "categories": [ + "patient_communication_contract", + "patient_mutation_delivery", + "notification_registration_delivery", + "native_patient_experience", + "error_offline_urgency" + ], + "decision_id": "patient_client_held" + }, + { + "path": "hummingbird/androidPatientApp/app/src/main/java/net/acumenus/hummingbird/patient/data/PatientSessionCoordinator.kt", + "sha256": "10bfbe8782746390ea71242d1c95ff5f09809b61be40533f91cba1e9acfaada2", + "surface": "legacy_android", + "categories": [ + "patient_communication_contract", + "patient_mutation_delivery", + "notification_registration_delivery", + "native_patient_experience", + "error_offline_urgency" + ], + "decision_id": "patient_client_held" + }, + { + "path": "hummingbird/androidPatientApp/app/src/main/java/net/acumenus/hummingbird/patient/PatientAppViewModel.kt", + "sha256": "dfa4d35f325b8199a66af67978e074aafd4364487944f5609a6ec93f5305d63e", + "surface": "legacy_android", + "categories": [ + "patient_communication_contract", + "patient_mutation_delivery", + "notification_registration_delivery", + "native_patient_experience", + "error_offline_urgency" + ], + "decision_id": "patient_client_held" + }, + { + "path": "hummingbird/androidPatientApp/app/src/main/java/net/acumenus/hummingbird/patient/PatientExperienceModels.kt", + "sha256": "4b9eb48815c34248d8f6580e4c72dd9c8a014ce6bce3f3810d82a2adadc8fcf8", + "surface": "legacy_android", + "categories": [ + "patient_communication_contract", + "patient_mutation_delivery", + "notification_registration_delivery", + "native_patient_experience", + "error_offline_urgency" + ], + "decision_id": "patient_client_held" + }, + { + "path": "hummingbird/androidPatientApp/app/src/main/java/net/acumenus/hummingbird/patient/ui/PatientExperienceScreen.kt", + "sha256": "7b2345a9735451514aec54fe52bf04d28964935c902bb1b13676e76e8065911d", + "surface": "legacy_android", + "categories": [ + "patient_communication_contract", + "patient_mutation_delivery", + "notification_registration_delivery", + "native_patient_experience", + "error_offline_urgency" + ], + "decision_id": "patient_client_held" + }, + { + "path": "hummingbird/androidPatientApp/app/src/main/java/net/acumenus/hummingbird/patient/ui/PatientMessagingPanel.kt", + "sha256": "950feca27fdca0823fe8a45ccc58fb976e61de1163324016d49c9cd3f97f25e5", + "surface": "legacy_android", + "categories": [ + "patient_communication_contract", + "patient_mutation_delivery", + "notification_registration_delivery", + "native_patient_experience", + "error_offline_urgency" + ], + "decision_id": "patient_client_held" + }, + { + "path": "hummingbird/androidPatientApp/app/src/test/java/net/acumenus/hummingbird/patient/data/PatientEndpointBoundaryTest.kt", + "sha256": "92edaed877c4cd3f2eb02eb3829bc8083a14cc84492592b9bbdc492d54c87c50", + "surface": "legacy_android_test", + "categories": [ + "patient_communication_contract", + "patient_mutation_delivery", + "notification_registration_delivery", + "native_patient_experience", + "error_offline_urgency" + ], + "decision_id": "test_evidence_only" + }, + { + "path": "hummingbird/androidPatientApp/app/src/test/java/net/acumenus/hummingbird/patient/data/PatientEnvelopeDecoderTest.kt", + "sha256": "ca83242060bce3c21d62e30ffa3a1bb9566373adb51c5b88788f2b62eb56e81d", + "surface": "legacy_android_test", + "categories": [ + "patient_communication_contract", + "patient_mutation_delivery", + "notification_registration_delivery", + "native_patient_experience", + "error_offline_urgency" + ], + "decision_id": "test_evidence_only" + }, + { + "path": "hummingbird/androidPatientApp/app/src/test/java/net/acumenus/hummingbird/patient/data/PatientSessionCoordinatorTest.kt", + "sha256": "7918d69d4e751e0f063c1061db1b3a1289a5d145fc5edb1a9571399d5408e9db", + "surface": "legacy_android_test", + "categories": [ + "patient_communication_contract", + "patient_mutation_delivery", + "notification_registration_delivery", + "native_patient_experience", + "error_offline_urgency" + ], + "decision_id": "test_evidence_only" + }, + { + "path": "hummingbird/androidPatientApp/app/src/test/java/net/acumenus/hummingbird/patient/PatientAppViewModelTest.kt", + "sha256": "58bfa794fda3f8b40c72d9f619ebe593668fc1379cafdbb7cb894a4ca468d0e6", + "surface": "legacy_android_test", + "categories": [ + "patient_communication_contract", + "patient_mutation_delivery", + "notification_registration_delivery", + "native_patient_experience", + "error_offline_urgency" + ], + "decision_id": "test_evidence_only" + }, + { + "path": "hummingbird/iosApp/Hummingbird/Features/PatientCommunications/PatientCommunicationDetailView.swift", + "sha256": "194ed05a9ae63fbfca4094773a074b9b693e1e85ac439d6af3a708e71796aa86", + "surface": "legacy_staff_ios", + "categories": [ + "staff_handoff_routing", + "native_patient_experience", + "error_offline_urgency" + ], + "decision_id": "staff_client_evidence_only" + }, + { + "path": "hummingbird/iosApp/Hummingbird/Features/PatientCommunications/PatientCommunicationRoutingView.swift", + "sha256": "8e39d299624db5ae9729ba32ef2342243af8fc5258377dc1c6566811bdef2732", + "surface": "legacy_staff_ios", + "categories": [ + "staff_handoff_routing", + "native_patient_experience", + "error_offline_urgency" + ], + "decision_id": "staff_client_evidence_only" + }, + { + "path": "hummingbird/iosApp/Hummingbird/Features/PatientCommunications/PatientCommunicationsView.swift", + "sha256": "3d2fc52b0bbaea4f0fc8422bb7ff502f056f7b4a841d2b3a9402b91d8781a023", + "surface": "legacy_staff_ios", + "categories": [ + "staff_handoff_routing", + "native_patient_experience", + "error_offline_urgency" + ], + "decision_id": "staff_client_evidence_only" + }, + { + "path": "hummingbird/iosApp/Hummingbird/Features/PatientCommunications/PatientCommunicationsViewModel.swift", + "sha256": "5836c9ebed6191e1824c68c7b6b7588b0c5ab56330a74ed3af42ea16a0df43c2", + "surface": "legacy_staff_ios", + "categories": [ + "staff_handoff_routing", + "native_patient_experience", + "error_offline_urgency" + ], + "decision_id": "staff_client_evidence_only" + }, + { + "path": "hummingbird/iosApp/Hummingbird/Networking/PatientCommunicationModels.swift", + "sha256": "694a1355fbbd4e6042f6ccb6789e194e0c61a6c54045677d9694413e8d74dd88", + "surface": "legacy_staff_ios", + "categories": [ + "staff_handoff_routing", + "native_patient_experience", + "error_offline_urgency" + ], + "decision_id": "staff_client_evidence_only" + }, + { + "path": "hummingbird/iosApp/HummingbirdTests/PatientCommunicationRoutingTests.swift", + "sha256": "4fd437719b9bcc7980dd2c3fd3ec8d54d7c0671634879adee6fd6a58f3a221ed", + "surface": "legacy_staff_ios", + "categories": [ + "staff_handoff_routing", + "native_patient_experience", + "error_offline_urgency" + ], + "decision_id": "staff_client_evidence_only" + }, + { + "path": "hummingbird/iosApp/HummingbirdTests/PatientCommunicationsTests.swift", + "sha256": "9693bd6a7ba0f9ee8665bf632d7848282a40929c62ab1c40e66119f77766a4da", + "surface": "legacy_staff_ios", + "categories": [ + "staff_handoff_routing", + "native_patient_experience", + "error_offline_urgency" + ], + "decision_id": "staff_client_evidence_only" + }, + { + "path": "hummingbird/iosApp/HummingbirdUITests/PatientCommunicationsUITests.swift", + "sha256": "f28a832bb5dda0fed43c0b55d4916ef093c58494235b33210d17e3ead807a5f0", + "surface": "legacy_staff_ios", + "categories": [ + "staff_handoff_routing", + "native_patient_experience", + "error_offline_urgency" + ], + "decision_id": "staff_client_evidence_only" + }, + { + "path": "hummingbird/iosPatientApp/HummingbirdPatient/App/PatientAppViewModel.swift", + "sha256": "74918da1c9af1a263b4535dec933c4c6acf4384bb7afa195997a26e5e1c37694", + "surface": "legacy_ios", + "categories": [ + "patient_communication_contract", + "patient_mutation_delivery", + "notification_registration_delivery", + "native_patient_experience", + "error_offline_urgency" + ], + "decision_id": "patient_client_held" + }, + { + "path": "hummingbird/iosPatientApp/HummingbirdPatient/Features/Messages/PatientMessagesView.swift", + "sha256": "1198deca7ff665bc59bcde856cea92468044eb4db43dc8e04d0bee679a5589fd", + "surface": "legacy_ios", + "categories": [ + "patient_communication_contract", + "patient_mutation_delivery", + "notification_registration_delivery", + "native_patient_experience", + "error_offline_urgency" + ], + "decision_id": "patient_client_held" + }, + { + "path": "hummingbird/iosPatientApp/HummingbirdPatient/Features/Path/PatientPathView.swift", + "sha256": "9943af445dba57ee99d8654c73e86a1ad8bb1225d785d33c06ddf337ae325ffd", + "surface": "legacy_ios", + "categories": [ + "patient_communication_contract", + "patient_mutation_delivery", + "notification_registration_delivery", + "native_patient_experience", + "error_offline_urgency" + ], + "decision_id": "patient_client_held" + }, + { + "path": "hummingbird/iosPatientApp/HummingbirdPatient/Models/PatientMessagingState.swift", + "sha256": "d7704dca1720c643850902cfc1330cb9975ef13f45cf94bbc1a1a1b034737c5a", + "surface": "legacy_ios", + "categories": [ + "patient_communication_contract", + "patient_mutation_delivery", + "notification_registration_delivery", + "native_patient_experience", + "error_offline_urgency" + ], + "decision_id": "patient_client_held" + }, + { + "path": "hummingbird/iosPatientApp/HummingbirdPatient/Networking/PatientAPIClient.swift", + "sha256": "72a8b54098bfd19402812aa0e2ef367154aa589d8f1bd7eb7f03b99b89ba9e8b", + "surface": "legacy_ios", + "categories": [ + "patient_communication_contract", + "patient_mutation_delivery", + "notification_registration_delivery", + "native_patient_experience", + "error_offline_urgency" + ], + "decision_id": "patient_client_held" + }, + { + "path": "hummingbird/iosPatientApp/HummingbirdPatient/Networking/PatientAPIModels.swift", + "sha256": "93c3250b7292c9abe17e3bb932e39c489332c8becc321663b4b8365a1e468759", + "surface": "legacy_ios", + "categories": [ + "patient_communication_contract", + "patient_mutation_delivery", + "notification_registration_delivery", + "native_patient_experience", + "error_offline_urgency" + ], + "decision_id": "patient_client_held" + }, + { + "path": "hummingbird/iosPatientApp/HummingbirdPatientTests/PatientAPIBoundaryTests.swift", + "sha256": "d50f0ad0f003447658df322d061d56157bf08e125d27ab2a4c51a1880c6aef41", + "surface": "legacy_ios_test", + "categories": [ + "patient_communication_contract", + "patient_mutation_delivery", + "notification_registration_delivery", + "native_patient_experience", + "error_offline_urgency" + ], + "decision_id": "test_evidence_only" + }, + { + "path": "hummingbird/iosPatientApp/HummingbirdPatientTests/PatientAPIClientTests.swift", + "sha256": "b96bfbdfca371903c5aba06a609a2c511798a08545ec5889b8d1c9ebf110c608", + "surface": "legacy_ios_test", + "categories": [ + "patient_communication_contract", + "patient_mutation_delivery", + "notification_registration_delivery", + "native_patient_experience", + "error_offline_urgency" + ], + "decision_id": "test_evidence_only" + }, + { + "path": "hummingbird/iosPatientApp/HummingbirdPatientTests/PatientAPIModelTests.swift", + "sha256": "2e461608ad1db8af66e204ba0396e864826adc71ebf4533d494390ebd138b95a", + "surface": "legacy_ios_test", + "categories": [ + "patient_communication_contract", + "patient_mutation_delivery", + "notification_registration_delivery", + "native_patient_experience", + "error_offline_urgency" + ], + "decision_id": "test_evidence_only" + }, + { + "path": "hummingbird/iosPatientApp/HummingbirdPatientTests/PatientAppViewModelTests.swift", + "sha256": "4938eefb40b613e3e43476d4fadc1bdbde04604155b8b268db25818ec2f58b26", + "surface": "legacy_ios_test", + "categories": [ + "patient_communication_contract", + "patient_mutation_delivery", + "notification_registration_delivery", + "native_patient_experience", + "error_offline_urgency" + ], + "decision_id": "test_evidence_only" + }, + { + "path": "hummingbird/iosPatientApp/HummingbirdPatientTests/PatientRoundsQuestionTopicTests.swift", + "sha256": "96865c13ed144ba09247d9c92a31bae4d45654822e76fd880707d040e2baac3e", + "surface": "legacy_ios_test", + "categories": [ + "patient_communication_contract", + "patient_mutation_delivery", + "notification_registration_delivery", + "native_patient_experience", + "error_offline_urgency" + ], + "decision_id": "test_evidence_only" + }, + { + "path": "hummingbird/iosPatientApp/HummingbirdPatientUITests/PatientReferenceJourneyUITests.swift", + "sha256": "879aabf062fb7ec70a0b71d02fc294b13e8e67d4f6349e67bbf1ac3feb9c23b0", + "surface": "legacy_ios_test", + "categories": [ + "patient_communication_contract", + "patient_mutation_delivery", + "notification_registration_delivery", + "native_patient_experience", + "error_offline_urgency" + ], + "decision_id": "test_evidence_only" + }, + { + "path": "resources/js/Pages/PatientCommunications/Index.tsx", + "sha256": "ec5f411bd49509df5be7f5d105482b4abe7253640d9700edca6cedee7ff95e32", + "surface": "legacy_staff_web", + "categories": [ + "staff_handoff_routing", + "native_patient_experience", + "error_offline_urgency" + ], + "decision_id": "staff_client_evidence_only" + }, + { + "path": "resources/js/Pages/PatientCommunications/mutationSafety.ts", + "sha256": "4b34f6445c1cabbc238a895244336af3e5d47cdec326f09588ae8b058b02abd9", + "surface": "legacy_staff_web", + "categories": [ + "staff_handoff_routing", + "native_patient_experience", + "error_offline_urgency" + ], + "decision_id": "staff_client_evidence_only" + }, + { + "path": "resources/js/Pages/PatientCommunications/RoutingControls.tsx", + "sha256": "4b86a4f601c52fa637362569929d9f1cc1b3dd29e6e9cdb1d96d9f3320604e81", + "surface": "legacy_staff_web", + "categories": [ + "staff_handoff_routing", + "native_patient_experience", + "error_offline_urgency" + ], + "decision_id": "staff_client_evidence_only" + }, + { + "path": "resources/js/Pages/PatientCommunications/routingPolicy.ts", + "sha256": "2aeb89966282dc77ab33dfa7bfed87c3b2dd5efb2ce42b6512d6408b15001d93", + "surface": "legacy_staff_web", + "categories": [ + "staff_handoff_routing", + "native_patient_experience", + "error_offline_urgency" + ], + "decision_id": "staff_client_evidence_only" + }, + { + "path": "routes/api.php", + "sha256": "a357886ae0975b63d978b975e53518a97d3f6e907820255a0f1c9e1b76a986ad", + "surface": "legacy_backend", + "categories": [ + "patient_communication_contract", + "patient_mutation_delivery", + "staff_handoff_routing", + "notification_registration_delivery", + "error_offline_urgency" + ], + "decision_id": "routes_rejected" + }, + { + "path": "routes/console.php", + "sha256": "609a681d3e5e068b826514cac0ecf320f4f4d1e99753243707f2941c9ebaaab4", + "surface": "legacy_backend", + "categories": [ + "patient_communication_contract", + "patient_mutation_delivery", + "staff_handoff_routing", + "notification_registration_delivery", + "error_offline_urgency" + ], + "decision_id": "routes_rejected" + }, + { + "path": "routes/patient.php", + "sha256": "e892a9d570668bec6ae54d18cbfd891f7d9df2e2c2f8f6e3689125d6b465af28", + "surface": "legacy_backend", + "categories": [ + "patient_communication_contract", + "patient_mutation_delivery", + "staff_handoff_routing", + "notification_registration_delivery", + "error_offline_urgency" + ], + "decision_id": "routes_rejected" + }, + { + "path": "routes/web.php", + "sha256": "270fdb18801b2fe1b17e64ee1b0092db0f7482ce417740196a165310c8a4ee21", + "surface": "legacy_backend", + "categories": [ + "patient_communication_contract", + "patient_mutation_delivery", + "staff_handoff_routing", + "notification_registration_delivery", + "error_offline_urgency" + ], + "decision_id": "routes_rejected" + }, + { + "path": "tests/Feature/Patient/PatientCommunicationEscalationServiceTest.php", + "sha256": "fe5c4fde7c0eaa9c6b7207281625db16a663fff62935f4f861fce74402bb6bec", + "surface": "legacy_test", + "categories": [ + "patient_communication_contract", + "patient_mutation_delivery", + "staff_handoff_routing", + "notification_registration_delivery", + "error_offline_urgency" + ], + "decision_id": "test_evidence_only" + }, + { + "path": "tests/Feature/Patient/PatientCommunicationLifecycleReconciliationServiceTest.php", + "sha256": "46702a03cd2c3701441de93a93f7097688596ae541ee04ea32e641a29c9896d0", + "surface": "legacy_test", + "categories": [ + "patient_communication_contract", + "patient_mutation_delivery", + "staff_handoff_routing", + "notification_registration_delivery", + "error_offline_urgency" + ], + "decision_id": "test_evidence_only" + }, + { + "path": "tests/Feature/Patient/PatientCommunicationPoolResolverTest.php", + "sha256": "b52128f82f5757263d90ebe4fb8a5ff7ec3b2e2fdd5f171fe1c9cc4ff77c3447", + "surface": "legacy_test", + "categories": [ + "patient_communication_contract", + "patient_mutation_delivery", + "staff_handoff_routing", + "notification_registration_delivery", + "error_offline_urgency" + ], + "decision_id": "test_evidence_only" + }, + { + "path": "tests/Feature/Patient/PatientEducationClarificationApiTest.php", + "sha256": "a25b13e76657090bfd20a90e145941575f353d6b1f12c9c637e28402b188416b", + "surface": "legacy_test", + "categories": [ + "patient_communication_contract", + "patient_mutation_delivery", + "staff_handoff_routing", + "notification_registration_delivery", + "error_offline_urgency" + ], + "decision_id": "test_evidence_only" + }, + { + "path": "tests/Feature/Patient/PatientMessagingApiTest.php", + "sha256": "6dd0da4f4fa0a2f5a2f764df09d630d78a0ceda6f42984f1d8269d6607857e8e", + "surface": "legacy_test", + "categories": [ + "patient_communication_contract", + "patient_mutation_delivery", + "staff_handoff_routing", + "notification_registration_delivery", + "error_offline_urgency" + ], + "decision_id": "test_evidence_only" + }, + { + "path": "tests/Feature/Patient/PatientProjectionOutboxTest.php", + "sha256": "763a801886bc93527ca34f78eee30e624f11d5379468a1c504c05c6945efc892", + "surface": "legacy_test", + "categories": [ + "patient_communication_contract", + "patient_mutation_delivery", + "staff_handoff_routing", + "notification_registration_delivery", + "error_offline_urgency" + ], + "decision_id": "test_evidence_only" + }, + { + "path": "tests/Feature/Patient/PatientStaffMessageHandoffConsumerTest.php", + "sha256": "6c7ecd157a4d571079a1943eaaac93bbe49e2aa3e290f847a2f91755848f7a6d", + "surface": "legacy_test", + "categories": [ + "patient_communication_contract", + "patient_mutation_delivery", + "staff_handoff_routing", + "notification_registration_delivery", + "error_offline_urgency" + ], + "decision_id": "test_evidence_only" + }, + { + "path": "tests/Feature/Patient/StaffPatientCommunicationApiTest.php", + "sha256": "962109f6dd6e9d37b02083bd85eb2305766f6b56167fb4fd3a55c70edf12cfb0", + "surface": "legacy_test", + "categories": [ + "patient_communication_contract", + "patient_mutation_delivery", + "staff_handoff_routing", + "notification_registration_delivery", + "error_offline_urgency" + ], + "decision_id": "test_evidence_only" + }, + { + "path": "tests/Feature/Patient/StaffPatientCommunicationWebTest.php", + "sha256": "cc9d9d24e0398df2da991c575511f265f383554862621f3c767d8b40524e5e64", + "surface": "legacy_test", + "categories": [ + "patient_communication_contract", + "patient_mutation_delivery", + "staff_handoff_routing", + "notification_registration_delivery", + "error_offline_urgency" + ], + "decision_id": "test_evidence_only" + }, + { + "path": "tests/js/pages/PatientCommunicationMutationSafety.test.ts", + "sha256": "4bb8a7129ca81c36d26632b5f2c412a0bdc5206aa42f777017e968ba6e3504d3", + "surface": "legacy_staff_web", + "categories": [ + "staff_handoff_routing", + "native_patient_experience", + "error_offline_urgency" + ], + "decision_id": "staff_client_evidence_only" + }, + { + "path": "tests/js/pages/PatientCommunicationRoutingPolicy.test.ts", + "sha256": "f1a59d447df5b3f98dc026ce2ecbe6d996af0a14ba3cba63493f0572501e5db8", + "surface": "legacy_staff_web", + "categories": [ + "staff_handoff_routing", + "native_patient_experience", + "error_offline_urgency" + ], + "decision_id": "staff_client_evidence_only" + }, + { + "path": "tests/js/pages/PatientCommunications.test.tsx", + "sha256": "53465b4999055e255406b42f8ef621c53961f8f7d90c97034d292883e8f614e9", + "surface": "legacy_staff_web", + "categories": [ + "staff_handoff_routing", + "native_patient_experience", + "error_offline_urgency" + ], + "decision_id": "staff_client_evidence_only" + }, + { + "path": "tests/js/pages/PatientCommunicationTransitionPolling.test.tsx", + "sha256": "3a9dd4e9c0f29668891c81784b3de22b9fa6cb819739d15612e2abed0079d3b7", + "surface": "legacy_staff_web", + "categories": [ + "staff_handoff_routing", + "native_patient_experience", + "error_offline_urgency" + ], + "decision_id": "staff_client_evidence_only" + } + ] +} diff --git a/docs/nightingale/migration/candidates/v0/journey-preference-presentation-release-source-classification.json b/docs/nightingale/migration/candidates/v0/journey-preference-presentation-release-source-classification.json new file mode 100644 index 00000000..3578247c --- /dev/null +++ b/docs/nightingale/migration/candidates/v0/journey-preference-presentation-release-source-classification.json @@ -0,0 +1,1913 @@ +{ + "schema_version": 1, + "classification_id": "nightingale-journey-preference-presentation-release-source-classification.v1", + "reviewed_source_commit": "45ddf907c0f15e378b37a1b0726724e346cb29fd", + "reviewed_at": "2026-07-27", + "status": "evidence_only_not_approved_for_implementation", + "scope": [ + "journey_projection_content", + "account_preferences", + "accessibility_presentation", + "synthetic_debug", + "release_packaging_identity", + "governance_audit_persistence", + "source_reconciliation_release", + "test_fixture_evidence" + ], + "product_universe": { + "definition": "Tracked legacy Hummingbird Patient native roots plus patient contract/backend/model/service/test/migration/command sources, excluding unrelated patient_flow migrations.", + "source_count": 256, + "inventory_digest": "a307e1957df7ef78eb61a9a9123f3902fd8929ebb3aaeb4dce48f2c88fb4a881", + "previously_classified_unique_count": 122, + "final_slice_count": 134, + "final_slice_inventory_digest": "4301de88a9071214001c2a58aa8fbc624bb49f24bb566428c8a1ef98aa44c13d", + "prior_manifests": [ + "docs/nightingale/migration/candidates/v0/source-classification.json", + "docs/nightingale/migration/candidates/v0/communication-notification-source-classification.json" + ], + "excluded_path_rules": [ + "database/migrations/*patient_flow*.php", + "staff/general mobile, web, and operational sources outside explicit prior communication evidence", + "generated build outputs and untracked emulator/tool caches" + ] + }, + "global_constraints": { + "implementation_permitted": false, + "runtime_adoption_permitted": false, + "route_registration_permitted": false, + "client_networking_permitted": false, + "legacy_identifier_reuse_permitted": false, + "legacy_asset_reuse_permitted": false, + "legacy_activation_reuse_permitted": false, + "synthetic_runtime_permitted": false, + "synthetic_release_content_permitted": false, + "reference_provisioning_permitted": false, + "patient_preference_persistence_permitted": false, + "patient_authored_mutation_permitted": false, + "pathway_source_adapter_permitted": false, + "pathway_draft_generation_permitted": false, + "pathway_release_permitted": false, + "migration_execution_permitted": false, + "production_data_used": false, + "production_query_permitted": false, + "production_replay_permitted": false, + "patient_or_principal_created": false, + "deployment_permitted": false + }, + "required_findings": { + "product_source_universe_count": 256, + "previously_classified_unique_count": 122, + "final_slice_source_count": 134, + "all_product_sources_classified": true, + "all_product_sources_approved_for_migration": false, + "server_and_native_preference_surfaces_match": false, + "native_locale_and_timezone_controls_present": false, + "patient_delivery_preferences_match_available_channels": false, + "android_reduced_motion_has_rendering_control_branch": false, + "ios_reduced_motion_controls_transition_or_background_motion": true, + "synthetic_reference_content_is_compile_excluded_from_release": true, + "reference_provisioning_is_approved_for_nightingale_or_production": false, + "legacy_release_configuration_is_nightingale_owned": false, + "patient_journeys_expose_empty_or_uncertain_released_content_patterns": true, + "legacy_first_record_encounter_selection_is_patient_safe": false, + "ios_journey_context_is_field_level_per_projection": false, + "android_composite_path_context_covers_every_displayed_projection": false, + "legacy_native_apps_expose_messages_as_top_level_navigation": true, + "nightingale_charter_approves_top_level_messages_navigation": false, + "independent_clinical_and_release_manager_controls_exist": true, + "two_person_release_control_covers_every_projection_kind": false, + "pathway_release_is_approved_for_nightingale": false, + "legacy_brand_assets_or_identifiers_are_reusable_in_nightingale": false + }, + "classifications": { + "reusable_safety_primitive": "A control objective may be independently reimplemented after approval; no legacy schema, code, identifier, copy, asset, or activation transfers.", + "reusable_product_behavior": "A patient need or behavior is a held design input only; its contract, content, state, ownership, authorization, and implementation remain unapproved.", + "test_fixture_only": "The source may inform synthetic tests, negative cases, or provenance only and can never become runtime, production, patient, or release content.", + "rejected_legacy_behavior": "The legacy product identity, activation, packaging, asset, provisioning, or behavior must not migrate into Nightingale." + }, + "dispositions": { + "principle_only": "Retain only the control objective and evidence. Reimplement it independently under Nightingale ownership after the applicable architecture and review gates; do not copy or activate the legacy source.", + "held": "Retain as candidate product behavior only. No Nightingale contract, route, client, copy, state, data access, or runtime behavior is authorized until the documented blockers and named approvals are closed.", + "test_only": "Use only as synthetic test-dimension or provenance evidence. It cannot authorize runtime behavior, release content, production data, a patient record, an endpoint, a provider, or deployment.", + "reject": "Do not migrate this legacy identity, packaging, activation, asset, provisioning, or runtime behavior into Nightingale. A new product-owned design and separate approval are required." + }, + "decisions": { + "pathway_draft_command_held": { + "classification": "reusable_product_behavior", + "disposition": "held", + "rationale": "The command demonstrates an explicit draft-only pathway workflow and machine-readable preview, but its Hummingbird configuration, source binding, content contract, and operational ownership are not Nightingale decisions. Retain the workflow as evidence and do not copy, register, or execute it.", + "categories": [ + "journey_projection_content", + "source_reconciliation_release" + ] + }, + "reference_provisioning_rejected": { + "classification": "rejected_legacy_behavior", + "disposition": "reject", + "rationale": "These command-accessible Hummingbird reference provisioners can create or bind synthetic operational encounters, principals, grants, enrollment challenges, secrets, policies, cursors, and draft projections. Their safeguards are useful audit evidence, but Nightingale must not inherit their identities, ownership markers, write paths, or production-runtime execution model.", + "categories": [ + "synthetic_debug", + "release_packaging_identity", + "governance_audit_persistence" + ] + }, + "account_preference_contract_held": { + "classification": "reusable_product_behavior", + "disposition": "held", + "rationale": "The request validates account presentation and delivery choices, but its seven-field server surface is not matched by either native preference editor and includes delivery channels without an implemented patient delivery capability. Nightingale needs separate presentation, locale, consent, and communication-preference ownership before adopting any field.", + "categories": [ + "account_preferences", + "accessibility_presentation" + ] + }, + "append_only_audit_safety_principle": { + "classification": "reusable_safety_primitive", + "disposition": "principle_only", + "rationale": "Append-only mutation resistance, external UUID assignment, content-free audit facts, source cursors, immutable status history, and explicit projection failure records are portable safety principles. The Hummingbird table names, model schemas, identifiers, and release semantics remain legacy-specific and are not approved Nightingale implementation.", + "categories": [ + "governance_audit_persistence", + "source_reconciliation_release" + ] + }, + "patient_authored_input_behavior_held": { + "classification": "reusable_product_behavior", + "disposition": "held", + "rationale": "The services preserve patient-authored preferences and personal goals as immutable, content-free associations to encrypted accountable messages and explicitly avoid clinical care-plan mutation. Nightingale may reuse that separation only after its messaging contract, idempotency, routing, language, review outcome, and patient-visible status are independently approved.", + "categories": [ + "journey_projection_content", + "governance_audit_persistence" + ] + }, + "projection_release_safety_principle": { + "classification": "reusable_safety_primitive", + "disposition": "principle_only", + "rationale": "Version-pinned drafts, immutable history, source reconciliation, independent clinical approval, a distinct catalog release manager, transactional release, and content-free actor digests are strong safety patterns. They do not prove that any pathway definition, source adapter, content, policy, reviewer, or release is approved for Nightingale.", + "categories": [ + "journey_projection_content", + "source_reconciliation_release", + "governance_audit_persistence" + ] + }, + "pathway_source_contract_held": { + "classification": "reusable_product_behavior", + "disposition": "held", + "rationale": "The source snapshot and status-observation value objects establish explicit source identity, observation time, version, and status rather than inferring a patient pathway from raw operational rows. Their vocabulary and adapter ownership remain unapproved, so Nightingale holds the behavior until a governed source contract and freshness policy exist.", + "categories": [ + "journey_projection_content", + "source_reconciliation_release" + ] + }, + "testing_only_projection_fixture": { + "classification": "test_fixture_only", + "disposition": "test_only", + "rationale": "The deterministic projection provisioner refuses non-testing environments and creates synthetic released content for automated verification. It is useful only as a source of test dimensions and must not become Nightingale runtime code, release content, a production fixture, or a shortcut around clinical/content approval.", + "categories": [ + "test_fixture_evidence", + "synthetic_debug", + "journey_projection_content" + ] + }, + "append_only_projection_schema_principle": { + "classification": "reusable_safety_primitive", + "disposition": "principle_only", + "rationale": "The migrations provide useful evidence for append-only patient-authored facts, pathway history, review/release separation, released-projection outbox creation, and database-level validation. Nightingale adopts no table, trigger, enum, policy, migration path, or production schema change from this classification.", + "categories": [ + "governance_audit_persistence", + "source_reconciliation_release", + "journey_projection_content" + ] + }, + "asset_provenance_evidence": { + "classification": "test_fixture_only", + "disposition": "test_only", + "rationale": "The legacy asset record is retained as non-runnable provenance and verification evidence. Its Hummingbird sources, derived files, visual ownership, and product identity do not transfer to Nightingale, whose separately supplied nightingale artwork and provenance remain authoritative.", + "categories": [ + "test_fixture_evidence", + "release_packaging_identity" + ] + }, + "generated_artifact_hygiene_principle": { + "classification": "reusable_safety_primitive", + "disposition": "principle_only", + "rationale": "Excluding generated Xcode products, derived data, archives, and per-user state from version control is a portable repository-safety principle. The legacy root and exact ignore file do not migrate; Nightingale retains its own independently governed build-output exclusions.", + "categories": [ + "release_packaging_identity" + ] + }, + "legacy_packaging_identity_rejected": { + "classification": "rejected_legacy_behavior", + "disposition": "reject", + "rationale": "These Xcode, Gradle, manifest, wrapper, signing, build, package, bundle, version, and pilot-release files are bound to the legacy Hummingbird Patient product. Nightingale already has independent targets and must not copy legacy identifiers, build activation, version values, signing assumptions, or release configuration.", + "categories": [ + "release_packaging_identity" + ] + }, + "test_fixture_evidence": { + "classification": "test_fixture_only", + "disposition": "test_only", + "rationale": "The source is an automated test or UI/instrumentation fixture. It may inform Nightingale test dimensions, negative cases, accessibility assertions, and platform verification, but its Hummingbird identifiers, fixtures, assertions, launch controls, and expected product behavior are not production implementation or approval evidence.", + "categories": [ + "test_fixture_evidence" + ] + }, + "debug_synthetic_fixture": { + "classification": "test_fixture_only", + "disposition": "test_only", + "rationale": "The source is compiled only into the Android debug source set and supplies emulator navigation or a clearly marked synthetic reference patient. Nightingale may design independent synthetic tests, but must not copy these extras, identifiers, payloads, wording, or any route from a debug scenario into a release artifact.", + "categories": [ + "test_fixture_evidence", + "synthetic_debug", + "journey_projection_content" + ] + }, + "native_journey_behavior_held": { + "classification": "reusable_product_behavior", + "disposition": "held", + "rationale": "The native source contains useful patient-centered journey, navigation, session, empty-state, provenance, freshness, urgent-help, or account behavior. It remains tied to Hummingbird models, copy, identifiers, activation, and contract assumptions; Nightingale must reissue it only after field-level contracts and named reviews.", + "categories": [ + "journey_projection_content", + "accessibility_presentation" + ] + }, + "native_privacy_configuration_principle": { + "classification": "reusable_safety_primitive", + "disposition": "principle_only", + "rationale": "Screen-capture protection, backup/transfer exclusion, cleartext-network denial, privacy-cover behavior, and platform-safe manifest configuration are portable defense principles. Nightingale must retain its independent package, threat model, test evidence, and release configuration rather than reuse these Hummingbird files.", + "categories": [ + "accessibility_presentation", + "release_packaging_identity", + "governance_audit_persistence" + ] + }, + "legacy_brand_assets_rejected": { + "classification": "rejected_legacy_behavior", + "disposition": "reject", + "rationale": "The images, app icon, launcher resources, colors, strings, and themes carry Hummingbird product identity or legacy scenic artwork. They are not Nightingale assets and cannot be copied or shipped in the independent patient product; only the separately governed Nightingale artwork and design system may be used.", + "categories": [ + "accessibility_presentation", + "release_packaging_identity" + ] + }, + "native_presentation_safety_principle": { + "classification": "reusable_safety_primitive", + "disposition": "principle_only", + "rationale": "Respecting a stronger system text-size setting, high-contrast presentation, reduced-transparency behavior, decorative-image semantics, privacy covers, and bounded motion are useful accessibility/privacy principles. Nightingale must independently implement and test them; the Android reduced-motion account choice currently has no rendering-control branch.", + "categories": [ + "accessibility_presentation", + "account_preferences" + ] + }, + "release_synthetic_exclusion_principle": { + "classification": "reusable_safety_primitive", + "disposition": "principle_only", + "rationale": "The Android release source set replaces launch hooks and synthetic content with inert stubs, providing strong evidence for compile-time exclusion. Nightingale should preserve the property with its own namespace, binary scans, and tests, never by copying the Hummingbird hook names or payload contract.", + "categories": [ + "synthetic_debug", + "release_packaging_identity" + ] + }, + "legacy_runtime_activation_rejected": { + "classification": "rejected_legacy_behavior", + "disposition": "reject", + "rationale": "The iOS configuration can select a live Hummingbird API through plist or process-environment values and validates the legacy production host. Nightingale rejects this activation mechanism, host ownership, keys, and endpoint boundary; any future client activation must be Nightingale-owned, signed-release governed, and independently reviewed.", + "categories": [ + "release_packaging_identity" + ] + } + }, + "sources": [ + { + "path": "app/Console/Commands/HummingbirdDraftPatientPathwayHistoryCommand.php", + "sha256": "2722dfd3f465588dd85fe7250a120708e84c9794a4e4596cd1c85daea3f343de", + "surface": "legacy_backend_command", + "classification": "reusable_product_behavior", + "disposition": "held", + "categories": [ + "journey_projection_content", + "source_reconciliation_release" + ], + "decision_id": "pathway_draft_command_held" + }, + { + "path": "app/Console/Commands/HummingbirdDraftReferencePatientProjectionsCommand.php", + "sha256": "f23285ed79c5363183cad44c5dbed9fdde596ca5c6d9ce3bf8a7cda2b30e5aca", + "surface": "legacy_backend_command", + "classification": "rejected_legacy_behavior", + "disposition": "reject", + "categories": [ + "governance_audit_persistence", + "release_packaging_identity", + "synthetic_debug" + ], + "decision_id": "reference_provisioning_rejected" + }, + { + "path": "app/Console/Commands/HummingbirdProvisionReferencePatientIdentityCommand.php", + "sha256": "3231005077cb48353cd0ae7bc48b7d57b054aa0c7174fdfaefbda8106bca54f5", + "surface": "legacy_backend_command", + "classification": "rejected_legacy_behavior", + "disposition": "reject", + "categories": [ + "governance_audit_persistence", + "release_packaging_identity", + "synthetic_debug" + ], + "decision_id": "reference_provisioning_rejected" + }, + { + "path": "app/Console/Commands/HummingbirdSeedReferencePatientCommand.php", + "sha256": "a11f6e146fd23c95b5df5b8f04c940642ce1ff0ea297062cb9e7eefddc55f50c", + "surface": "legacy_backend_command", + "classification": "rejected_legacy_behavior", + "disposition": "reject", + "categories": [ + "governance_audit_persistence", + "release_packaging_identity", + "synthetic_debug" + ], + "decision_id": "reference_provisioning_rejected" + }, + { + "path": "app/Http/Requests/Patient/UpdatePreferencesRequest.php", + "sha256": "3e5393e52096e28d97a6423eca20449127a30c03b677ddd1aa9a8daaefc65830", + "surface": "legacy_backend_request", + "classification": "reusable_product_behavior", + "disposition": "held", + "categories": [ + "accessibility_presentation", + "account_preferences" + ], + "decision_id": "account_preference_contract_held" + }, + { + "path": "app/Models/Patient/Concerns/AssignsExternalUuid.php", + "sha256": "58faf172a142fbb43184483499798dc92225663644a5661ae7fa4604146d4f45", + "surface": "legacy_backend_model", + "classification": "reusable_safety_primitive", + "disposition": "principle_only", + "categories": [ + "governance_audit_persistence", + "source_reconciliation_release" + ], + "decision_id": "append_only_audit_safety_principle" + }, + { + "path": "app/Models/Patient/Concerns/IsAppendOnly.php", + "sha256": "54d439af7e7866aea7afbf655c97fac1cf149f193118fe9d13e4e616a98d598f", + "surface": "legacy_backend_model", + "classification": "reusable_safety_primitive", + "disposition": "principle_only", + "categories": [ + "governance_audit_persistence", + "source_reconciliation_release" + ], + "decision_id": "append_only_audit_safety_principle" + }, + { + "path": "app/Models/Patient/PatientAccessAuditEvent.php", + "sha256": "2b1145254cfdc8cf9fa6f4d766d9cf2b64d60fc3450a17a74d3727e5cf2ab00d", + "surface": "legacy_backend_model", + "classification": "reusable_safety_primitive", + "disposition": "principle_only", + "categories": [ + "governance_audit_persistence", + "source_reconciliation_release" + ], + "decision_id": "append_only_audit_safety_principle" + }, + { + "path": "app/Models/Patient/PatientAuthoredGoal.php", + "sha256": "14228339e0b7d237905a2fe0ffe2936c8cf8a11c45763fa4769ba58bd60d483b", + "surface": "legacy_backend_model", + "classification": "reusable_product_behavior", + "disposition": "held", + "categories": [ + "governance_audit_persistence", + "journey_projection_content" + ], + "decision_id": "patient_authored_input_behavior_held" + }, + { + "path": "app/Models/Patient/PatientCarePreference.php", + "sha256": "195bf19d9e90656b6f8224c6ec59d544ccb91a64d6d832429cf435d50995f4e6", + "surface": "legacy_backend_model", + "classification": "reusable_product_behavior", + "disposition": "held", + "categories": [ + "governance_audit_persistence", + "journey_projection_content" + ], + "decision_id": "patient_authored_input_behavior_held" + }, + { + "path": "app/Models/Patient/PatientContentAction.php", + "sha256": "6d4e8d61504732a354483d6b659313495d53646f0d12213cabe09e30e7acf87c", + "surface": "legacy_backend_model", + "classification": "reusable_safety_primitive", + "disposition": "principle_only", + "categories": [ + "governance_audit_persistence", + "source_reconciliation_release" + ], + "decision_id": "append_only_audit_safety_principle" + }, + { + "path": "app/Models/Patient/PatientPathwayInstance.php", + "sha256": "50d97b6d03a24c7f757efcc38075b18d004487cd15f570edf3d005eb7cf4afea", + "surface": "legacy_backend_model", + "classification": "reusable_safety_primitive", + "disposition": "principle_only", + "categories": [ + "governance_audit_persistence", + "source_reconciliation_release" + ], + "decision_id": "append_only_audit_safety_principle" + }, + { + "path": "app/Models/Patient/PatientPathwayMilestoneInstance.php", + "sha256": "5d9617d0b6532bfcff4520a2878ac450a28bd9c57c45006b3b469b8bdc2bb1f9", + "surface": "legacy_backend_model", + "classification": "reusable_safety_primitive", + "disposition": "principle_only", + "categories": [ + "governance_audit_persistence", + "source_reconciliation_release" + ], + "decision_id": "append_only_audit_safety_principle" + }, + { + "path": "app/Models/Patient/PatientPathwayMilestoneStatusEvent.php", + "sha256": "b70b6276b2f16caa04376290222b174b202f0188384fd957c60a77896f708546", + "surface": "legacy_backend_model", + "classification": "reusable_safety_primitive", + "disposition": "principle_only", + "categories": [ + "governance_audit_persistence", + "source_reconciliation_release" + ], + "decision_id": "append_only_audit_safety_principle" + }, + { + "path": "app/Models/Patient/PatientPathwayProjectionReleaseExecution.php", + "sha256": "b56d60248253e54c80b1ab3e94f42f3c1563f52b2e061b9d0b0b9c86c85fdd0d", + "surface": "legacy_backend_model", + "classification": "reusable_safety_primitive", + "disposition": "principle_only", + "categories": [ + "governance_audit_persistence", + "source_reconciliation_release" + ], + "decision_id": "append_only_audit_safety_principle" + }, + { + "path": "app/Models/Patient/PatientPathwayProjectionReview.php", + "sha256": "e221c2d530e4e21ccc2175648be538c874ba3aacdd449a13998cd178ce493d8b", + "surface": "legacy_backend_model", + "classification": "reusable_safety_primitive", + "disposition": "principle_only", + "categories": [ + "governance_audit_persistence", + "source_reconciliation_release" + ], + "decision_id": "append_only_audit_safety_principle" + }, + { + "path": "app/Models/Patient/PatientPathwayStageInstance.php", + "sha256": "bd293f47a9cf7f911299b5658bbdfa8d2db7f6d3e04b9f2bc7c6f21788d7ebd2", + "surface": "legacy_backend_model", + "classification": "reusable_safety_primitive", + "disposition": "principle_only", + "categories": [ + "governance_audit_persistence", + "source_reconciliation_release" + ], + "decision_id": "append_only_audit_safety_principle" + }, + { + "path": "app/Models/Patient/PatientPathwayStageStatusEvent.php", + "sha256": "e581b0706b6970666987534f63ea04c408eef91b762e54529a948c0a081bb55b", + "surface": "legacy_backend_model", + "classification": "reusable_safety_primitive", + "disposition": "principle_only", + "categories": [ + "governance_audit_persistence", + "source_reconciliation_release" + ], + "decision_id": "append_only_audit_safety_principle" + }, + { + "path": "app/Models/Patient/PatientProjectionCursor.php", + "sha256": "d3cc1516fe99ca10ed2f756d5abcf30b0e6bb071fa05b963b38994220b465c1d", + "surface": "legacy_backend_model", + "classification": "reusable_safety_primitive", + "disposition": "principle_only", + "categories": [ + "governance_audit_persistence", + "source_reconciliation_release" + ], + "decision_id": "append_only_audit_safety_principle" + }, + { + "path": "app/Models/Patient/PatientProjectionFailure.php", + "sha256": "78cf944aacfecc0cfe64b59242cbbc9c70eba3bd55fd6e6394bae9b8997a28d4", + "surface": "legacy_backend_model", + "classification": "reusable_safety_primitive", + "disposition": "principle_only", + "categories": [ + "governance_audit_persistence", + "source_reconciliation_release" + ], + "decision_id": "append_only_audit_safety_principle" + }, + { + "path": "app/Services/Patient/Demo/HummingbirdPatientReferenceIdentityProvisioner.php", + "sha256": "6a2927a9e5f59c177668cb16c9c81bba6c64b97b6d99e69d405bbcfd87ae1eb3", + "surface": "legacy_backend_service", + "classification": "rejected_legacy_behavior", + "disposition": "reject", + "categories": [ + "governance_audit_persistence", + "release_packaging_identity", + "synthetic_debug" + ], + "decision_id": "reference_provisioning_rejected" + }, + { + "path": "app/Services/Patient/Demo/HummingbirdPatientReferenceProjectionDraftProvisioner.php", + "sha256": "7ec1971fff4007ca5457a76419d37136f80be51582794bb27e1b559e2a142abf", + "surface": "legacy_backend_service", + "classification": "rejected_legacy_behavior", + "disposition": "reject", + "categories": [ + "governance_audit_persistence", + "release_packaging_identity", + "synthetic_debug" + ], + "decision_id": "reference_provisioning_rejected" + }, + { + "path": "app/Services/Patient/Demo/PatientEnrollmentMaterialGenerator.php", + "sha256": "a41fd36b887b5548e570551d615f52f3732415de7084d81af49a68962badb94d", + "surface": "legacy_backend_service", + "classification": "rejected_legacy_behavior", + "disposition": "reject", + "categories": [ + "governance_audit_persistence", + "release_packaging_identity", + "synthetic_debug" + ], + "decision_id": "reference_provisioning_rejected" + }, + { + "path": "app/Services/Patient/Goals/PatientAuthoredGoalService.php", + "sha256": "504eeddaac0270447adc9cd9a4f5d17310ab78458b8066bd7cedaeeaea7f9c87", + "surface": "legacy_backend_service", + "classification": "reusable_product_behavior", + "disposition": "held", + "categories": [ + "governance_audit_persistence", + "journey_projection_content" + ], + "decision_id": "patient_authored_input_behavior_held" + }, + { + "path": "app/Services/Patient/Pathway/PatientPathwayInstanceService.php", + "sha256": "8a79e6de936ac2d33f2ae499369c6bb57db70a9f6f544129fe10de90a4181d28", + "surface": "legacy_backend_service", + "classification": "reusable_safety_primitive", + "disposition": "principle_only", + "categories": [ + "governance_audit_persistence", + "journey_projection_content", + "source_reconciliation_release" + ], + "decision_id": "projection_release_safety_principle" + }, + { + "path": "app/Services/Patient/Pathway/PatientPathwaySourceReconciliationService.php", + "sha256": "3f7bfbfefcfb8aede209df6f559b93a79f90a6d361f064308c98530a204b67fc", + "surface": "legacy_backend_service", + "classification": "reusable_safety_primitive", + "disposition": "principle_only", + "categories": [ + "governance_audit_persistence", + "journey_projection_content", + "source_reconciliation_release" + ], + "decision_id": "projection_release_safety_principle" + }, + { + "path": "app/Services/Patient/Pathway/Source/PatientPathwaySourceSnapshot.php", + "sha256": "ffb4eb4f65436f0cad56722a22893cca024a0b81efc334c9135eeedb96118661", + "surface": "legacy_backend_service", + "classification": "reusable_product_behavior", + "disposition": "held", + "categories": [ + "journey_projection_content", + "source_reconciliation_release" + ], + "decision_id": "pathway_source_contract_held" + }, + { + "path": "app/Services/Patient/Pathway/Source/PatientPathwaySourceStatusObservation.php", + "sha256": "c3e21b45df4d405fda9d3286249967b4c7e42073391180e93039327bcd04f6cb", + "surface": "legacy_backend_service", + "classification": "reusable_product_behavior", + "disposition": "held", + "categories": [ + "journey_projection_content", + "source_reconciliation_release" + ], + "decision_id": "pathway_source_contract_held" + }, + { + "path": "app/Services/Patient/PatientAccessAuditRecorder.php", + "sha256": "ce6bf3ba97d46912da9a8780f3a672d4740cffd6441db2db4ce301e8334fd768", + "surface": "legacy_backend_service", + "classification": "reusable_safety_primitive", + "disposition": "principle_only", + "categories": [ + "governance_audit_persistence", + "source_reconciliation_release" + ], + "decision_id": "append_only_audit_safety_principle" + }, + { + "path": "app/Services/Patient/PatientHmac.php", + "sha256": "e1ac437e8c2ba7251583c5bf6dea86f0c7a2d2ebab5fcdf497d2f6c8e733626e", + "surface": "legacy_backend_service", + "classification": "reusable_safety_primitive", + "disposition": "principle_only", + "categories": [ + "governance_audit_persistence", + "source_reconciliation_release" + ], + "decision_id": "append_only_audit_safety_principle" + }, + { + "path": "app/Services/Patient/Preferences/PatientCarePreferenceService.php", + "sha256": "1b8c85e8b47f9bf09c6a3889e35cf18823981d978b47bb5fca9bbc756c71887f", + "surface": "legacy_backend_service", + "classification": "reusable_product_behavior", + "disposition": "held", + "categories": [ + "governance_audit_persistence", + "journey_projection_content" + ], + "decision_id": "patient_authored_input_behavior_held" + }, + { + "path": "app/Services/Patient/Projection/PatientPathwayHistoryDraftService.php", + "sha256": "692584f77a23b3abe82388b8272cdf74dbc63dc3fd0aca7123ce55cc3126e915", + "surface": "legacy_backend_service", + "classification": "reusable_safety_primitive", + "disposition": "principle_only", + "categories": [ + "governance_audit_persistence", + "journey_projection_content", + "source_reconciliation_release" + ], + "decision_id": "projection_release_safety_principle" + }, + { + "path": "app/Services/Patient/Projection/PatientPathwayProjectionReviewReleaseService.php", + "sha256": "a7c005e4a159149299860def527956c02784e1a08b30d15973204315043884b7", + "surface": "legacy_backend_service", + "classification": "reusable_safety_primitive", + "disposition": "principle_only", + "categories": [ + "governance_audit_persistence", + "journey_projection_content", + "source_reconciliation_release" + ], + "decision_id": "projection_release_safety_principle" + }, + { + "path": "app/Services/Patient/Projection/SyntheticPatientProjectionProvisioner.php", + "sha256": "033dfcf5c06e28cb834b1d748ec72f7f555d13f17d546d521454ef71a5edd550", + "surface": "legacy_backend_service", + "classification": "test_fixture_only", + "disposition": "test_only", + "categories": [ + "journey_projection_content", + "synthetic_debug", + "test_fixture_evidence" + ], + "decision_id": "testing_only_projection_fixture" + }, + { + "path": "database/migrations/2026_07_20_000500_extend_patient_projection_kinds_for_pathway_events.php", + "sha256": "2cd91e09cfb954f059ee2a6bfdddbe1fc7090183f6cce45bb4d79433dc81ed4c", + "surface": "legacy_database", + "classification": "reusable_safety_primitive", + "disposition": "principle_only", + "categories": [ + "governance_audit_persistence", + "journey_projection_content", + "source_reconciliation_release" + ], + "decision_id": "append_only_projection_schema_principle" + }, + { + "path": "database/migrations/2026_07_21_000600_extend_patient_projection_kinds_for_rounds_summary.php", + "sha256": "c7283854ce77bc7025d473283f7a713d83516b6a4a669ae577f69e5a1b3837bb", + "surface": "legacy_database", + "classification": "reusable_safety_primitive", + "disposition": "principle_only", + "categories": [ + "governance_audit_persistence", + "journey_projection_content", + "source_reconciliation_release" + ], + "decision_id": "append_only_projection_schema_principle" + }, + { + "path": "database/migrations/2026_07_21_000700_create_patient_round_question_promotions.php", + "sha256": "9a2834841c3915c9cb670eb8bd87c234089b8887aface6d9bf740e7c46c17406", + "surface": "legacy_database", + "classification": "reusable_safety_primitive", + "disposition": "principle_only", + "categories": [ + "governance_audit_persistence", + "journey_projection_content", + "source_reconciliation_release" + ], + "decision_id": "append_only_projection_schema_principle" + }, + { + "path": "database/migrations/2026_07_21_000800_create_patient_round_question_promotion_outcomes.php", + "sha256": "cefd3ce2c45674235e93a68d7fbe729fc2f8c3ce9a99af868c66ae7b022b828b", + "surface": "legacy_database", + "classification": "reusable_safety_primitive", + "disposition": "principle_only", + "categories": [ + "governance_audit_persistence", + "journey_projection_content", + "source_reconciliation_release" + ], + "decision_id": "append_only_projection_schema_principle" + }, + { + "path": "database/migrations/2026_07_22_001200_create_patient_education_clarification_requests.php", + "sha256": "03f6687f91b9f4070348e4ca691f33308c38350541e4130f1d286a0dfffc55ee", + "surface": "legacy_database", + "classification": "reusable_safety_primitive", + "disposition": "principle_only", + "categories": [ + "governance_audit_persistence", + "journey_projection_content", + "source_reconciliation_release" + ], + "decision_id": "append_only_projection_schema_principle" + }, + { + "path": "database/migrations/2026_07_22_001300_create_patient_care_preferences.php", + "sha256": "88f20542caa3c83bb9e8d658dd947abc12e9c9ae5c392019b6b80458466dfeef", + "surface": "legacy_database", + "classification": "reusable_safety_primitive", + "disposition": "principle_only", + "categories": [ + "governance_audit_persistence", + "journey_projection_content", + "source_reconciliation_release" + ], + "decision_id": "append_only_projection_schema_principle" + }, + { + "path": "database/migrations/2026_07_22_001400_create_patient_pathway_instance_history.php", + "sha256": "55ee479ac91c48c8a40ad20f6b50ee38fd1dc27f4b67b11ed6af0c29bb86497c", + "surface": "legacy_database", + "classification": "reusable_safety_primitive", + "disposition": "principle_only", + "categories": [ + "governance_audit_persistence", + "journey_projection_content", + "source_reconciliation_release" + ], + "decision_id": "append_only_projection_schema_principle" + }, + { + "path": "database/migrations/2026_07_22_001500_enqueue_released_patient_projection_outbox.php", + "sha256": "6bc0c7f235582f1a0d26db810876167cfc8b68d68412cf9cb728717fccecd1c1", + "surface": "legacy_database", + "classification": "reusable_safety_primitive", + "disposition": "principle_only", + "categories": [ + "governance_audit_persistence", + "journey_projection_content", + "source_reconciliation_release" + ], + "decision_id": "append_only_projection_schema_principle" + }, + { + "path": "database/migrations/2026_07_22_001600_create_patient_authored_goals.php", + "sha256": "9d229ffcee4d79c0978c05528a77cbc4dc8cadd1000dfcef2b137ff1de2c47c7", + "surface": "legacy_database", + "classification": "reusable_safety_primitive", + "disposition": "principle_only", + "categories": [ + "governance_audit_persistence", + "journey_projection_content", + "source_reconciliation_release" + ], + "decision_id": "append_only_projection_schema_principle" + }, + { + "path": "database/migrations/2026_07_22_001700_create_patient_pathway_projection_review_release.php", + "sha256": "f3c3ccbac7ca7fd341f87f7da6bf322633ae64692cce57fd9d5ae3fcbc30b972", + "surface": "legacy_database", + "classification": "reusable_safety_primitive", + "disposition": "principle_only", + "categories": [ + "governance_audit_persistence", + "journey_projection_content", + "source_reconciliation_release" + ], + "decision_id": "append_only_projection_schema_principle" + }, + { + "path": "database/migrations/2026_07_22_001900_reconcile_patient_projection_database_guards.php", + "sha256": "fba120b76b034e10bfc1ae9295a910c72aa2449060f00bd0d0edd84859210765", + "surface": "legacy_database", + "classification": "reusable_safety_primitive", + "disposition": "principle_only", + "categories": [ + "governance_audit_persistence", + "journey_projection_content", + "source_reconciliation_release" + ], + "decision_id": "append_only_projection_schema_principle" + }, + { + "path": "hummingbird/androidPatientApp/ASSET_PROVENANCE.md", + "sha256": "9e7e7c43540343c60127e9df6995616fff36185a64fcbc54640f664aeeb129c7", + "surface": "legacy_android_evidence", + "classification": "test_fixture_only", + "disposition": "test_only", + "categories": [ + "release_packaging_identity", + "test_fixture_evidence" + ], + "decision_id": "asset_provenance_evidence" + }, + { + "path": "hummingbird/androidPatientApp/app/build.gradle.kts", + "sha256": "95cf08272810438c9ce78bae4424ca60a9cfb8e5b0b3852466a076c476598072", + "surface": "legacy_android_packaging", + "classification": "rejected_legacy_behavior", + "disposition": "reject", + "categories": [ + "release_packaging_identity" + ], + "decision_id": "legacy_packaging_identity_rejected" + }, + { + "path": "hummingbird/androidPatientApp/app/proguard-rules.pro", + "sha256": "0c24a7210c5d2eff1ce1b3a292ab770497a73e6206cb5254f65567132eef6656", + "surface": "legacy_android_packaging", + "classification": "rejected_legacy_behavior", + "disposition": "reject", + "categories": [ + "release_packaging_identity" + ], + "decision_id": "legacy_packaging_identity_rejected" + }, + { + "path": "hummingbird/androidPatientApp/app/src/androidTest/java/net/acumenus/hummingbird/patient/PatientAuthenticationSmokeTest.kt", + "sha256": "cfca5bb98b7c9528b8d85e2c2f2e44c4eb6cf1da8265ac200cd0219f04169229", + "surface": "legacy_android_test", + "classification": "test_fixture_only", + "disposition": "test_only", + "categories": [ + "governance_audit_persistence", + "release_packaging_identity", + "test_fixture_evidence" + ], + "decision_id": "test_fixture_evidence" + }, + { + "path": "hummingbird/androidPatientApp/app/src/androidTest/java/net/acumenus/hummingbird/patient/PatientSecureStorageInstrumentedTest.kt", + "sha256": "aca1ca0a01c0d81a453d08392f3b6fe13f00338c23d24d2a3aafc7d87564cd80", + "surface": "legacy_android_test", + "classification": "test_fixture_only", + "disposition": "test_only", + "categories": [ + "governance_audit_persistence", + "release_packaging_identity", + "test_fixture_evidence" + ], + "decision_id": "test_fixture_evidence" + }, + { + "path": "hummingbird/androidPatientApp/app/src/androidTest/java/net/acumenus/hummingbird/patient/PatientSessionManagementInstrumentedTest.kt", + "sha256": "224fb3b4b9776104327f96421c926f455cc17d035e40183186875786a818c543", + "surface": "legacy_android_test", + "classification": "test_fixture_only", + "disposition": "test_only", + "categories": [ + "governance_audit_persistence", + "release_packaging_identity", + "test_fixture_evidence" + ], + "decision_id": "test_fixture_evidence" + }, + { + "path": "hummingbird/androidPatientApp/app/src/debug/java/net/acumenus/hummingbird/patient/PatientLaunchHooks.kt", + "sha256": "d9d2ac9746f2f573b6abd7924e2c2564cfad77dd10417b56166f4ff162097546", + "surface": "legacy_android_debug_source", + "classification": "test_fixture_only", + "disposition": "test_only", + "categories": [ + "journey_projection_content", + "synthetic_debug", + "test_fixture_evidence" + ], + "decision_id": "debug_synthetic_fixture" + }, + { + "path": "hummingbird/androidPatientApp/app/src/debug/java/net/acumenus/hummingbird/patient/SyntheticReferencePatientScenario.kt", + "sha256": "c6b8d8d3018c4c476555c7d01603fdc681e7ed4d11e9d8244e5b0de877446741", + "surface": "legacy_android_debug_source", + "classification": "test_fixture_only", + "disposition": "test_only", + "categories": [ + "journey_projection_content", + "synthetic_debug", + "test_fixture_evidence" + ], + "decision_id": "debug_synthetic_fixture" + }, + { + "path": "hummingbird/androidPatientApp/app/src/main/java/net/acumenus/hummingbird/patient/MainActivity.kt", + "sha256": "dcd9b7dd11f0db0078e9044ff2ef99f00adedc344f3408301362c7061b5fc884", + "surface": "legacy_android_source", + "classification": "reusable_product_behavior", + "disposition": "held", + "categories": [ + "accessibility_presentation", + "journey_projection_content" + ], + "decision_id": "native_journey_behavior_held" + }, + { + "path": "hummingbird/androidPatientApp/app/src/main/java/net/acumenus/hummingbird/patient/PatientLaunchState.kt", + "sha256": "a64803162032d9add6c538310d23f6cee67194e5c4129a7e4d5031648e98db16", + "surface": "legacy_android_source", + "classification": "reusable_product_behavior", + "disposition": "held", + "categories": [ + "accessibility_presentation", + "journey_projection_content" + ], + "decision_id": "native_journey_behavior_held" + }, + { + "path": "hummingbird/androidPatientApp/app/src/main/java/net/acumenus/hummingbird/patient/PatientPrivacyPolicy.kt", + "sha256": "2d77e58a3601bb1b73d8340aba0e36c36433d2a98bdd2f3a4563f8051006790b", + "surface": "legacy_android_source", + "classification": "reusable_safety_primitive", + "disposition": "principle_only", + "categories": [ + "accessibility_presentation", + "governance_audit_persistence", + "release_packaging_identity" + ], + "decision_id": "native_privacy_configuration_principle" + }, + { + "path": "hummingbird/androidPatientApp/app/src/main/java/net/acumenus/hummingbird/patient/ui/HummingbirdPatientTheme.kt", + "sha256": "d73b9832ca33cd8d1a98c7a7d6447a89648f307141bc222ca7cc4d999f97d805", + "surface": "legacy_android_source", + "classification": "rejected_legacy_behavior", + "disposition": "reject", + "categories": [ + "accessibility_presentation", + "release_packaging_identity" + ], + "decision_id": "legacy_brand_assets_rejected" + }, + { + "path": "hummingbird/androidPatientApp/app/src/main/java/net/acumenus/hummingbird/patient/ui/PatientApp.kt", + "sha256": "a12e7e36c267a4013e40b31e5242924548df45c273d40989b83458530d72f3bb", + "surface": "legacy_android_source", + "classification": "reusable_product_behavior", + "disposition": "held", + "categories": [ + "accessibility_presentation", + "journey_projection_content" + ], + "decision_id": "native_journey_behavior_held" + }, + { + "path": "hummingbird/androidPatientApp/app/src/main/java/net/acumenus/hummingbird/patient/ui/PatientPreferencesScreen.kt", + "sha256": "1c884c0b166332bfb5c00c0ce9ae5228c3d57656b947c4324caa21b456cea8cc", + "surface": "legacy_android_source", + "classification": "reusable_product_behavior", + "disposition": "held", + "categories": [ + "accessibility_presentation", + "journey_projection_content" + ], + "decision_id": "native_journey_behavior_held" + }, + { + "path": "hummingbird/androidPatientApp/app/src/main/java/net/acumenus/hummingbird/patient/ui/PatientPresentationAccessibility.kt", + "sha256": "e7c2b724599b1d26332579a7d6a853e4c970691f21f3ef1d9ebca324a1720992", + "surface": "legacy_android_source", + "classification": "reusable_safety_primitive", + "disposition": "principle_only", + "categories": [ + "accessibility_presentation", + "account_preferences" + ], + "decision_id": "native_presentation_safety_principle" + }, + { + "path": "hummingbird/androidPatientApp/app/src/main/java/net/acumenus/hummingbird/patient/ui/PatientScenicBackground.kt", + "sha256": "55e406bf0144d722f8ecb6d645d7c692a6e41a95fa35f880798ee205544a0e34", + "surface": "legacy_android_source", + "classification": "reusable_safety_primitive", + "disposition": "principle_only", + "categories": [ + "accessibility_presentation", + "account_preferences" + ], + "decision_id": "native_presentation_safety_principle" + }, + { + "path": "hummingbird/androidPatientApp/app/src/main/java/net/acumenus/hummingbird/patient/ui/PatientSessionManagementScreen.kt", + "sha256": "1f1f6da3814bd9070f8488790f8ab9f117c710a482629cc826a9d7d4e779a185", + "surface": "legacy_android_source", + "classification": "reusable_product_behavior", + "disposition": "held", + "categories": [ + "accessibility_presentation", + "journey_projection_content" + ], + "decision_id": "native_journey_behavior_held" + }, + { + "path": "hummingbird/androidPatientApp/app/src/main/res/drawable-nodpi/patient_hummingbird_airy_flight.jpg", + "sha256": "a400063289b0ec8b62a7059c3e1e10618da545fe50871b2f3ddb35935e958117", + "surface": "legacy_android_resource", + "classification": "rejected_legacy_behavior", + "disposition": "reject", + "categories": [ + "accessibility_presentation", + "release_packaging_identity" + ], + "decision_id": "legacy_brand_assets_rejected" + }, + { + "path": "hummingbird/androidPatientApp/app/src/main/res/drawable-nodpi/patient_hummingbird_calm_green.jpg", + "sha256": "d5dc322481721ac5a29a6fe34c777bf727385dcd9d3d5aad1674182b5ccbec2d", + "surface": "legacy_android_resource", + "classification": "rejected_legacy_behavior", + "disposition": "reject", + "categories": [ + "accessibility_presentation", + "release_packaging_identity" + ], + "decision_id": "legacy_brand_assets_rejected" + }, + { + "path": "hummingbird/androidPatientApp/app/src/main/res/drawable-nodpi/patient_hummingbird_care_connection.jpg", + "sha256": "b70ba3d0626f27bd29311a3e70a8e306768fd3c316cfae2554765b89603dcfbd", + "surface": "legacy_android_resource", + "classification": "rejected_legacy_behavior", + "disposition": "reject", + "categories": [ + "accessibility_presentation", + "release_packaging_identity" + ], + "decision_id": "legacy_brand_assets_rejected" + }, + { + "path": "hummingbird/androidPatientApp/app/src/main/res/drawable-nodpi/patient_hummingbird_warm_motion.jpg", + "sha256": "b3f127a8fbd2754d69adc12432fc01f22a9096ee8850b8fdab10e769e5105fb8", + "surface": "legacy_android_resource", + "classification": "rejected_legacy_behavior", + "disposition": "reject", + "categories": [ + "accessibility_presentation", + "release_packaging_identity" + ], + "decision_id": "legacy_brand_assets_rejected" + }, + { + "path": "hummingbird/androidPatientApp/app/src/main/res/drawable/ic_launcher_foreground.xml", + "sha256": "b9b7708f35ce3e0521d89b286297787e6bf489797c55f9f0c5b4d954a715e2b6", + "surface": "legacy_android_resource", + "classification": "rejected_legacy_behavior", + "disposition": "reject", + "categories": [ + "accessibility_presentation", + "release_packaging_identity" + ], + "decision_id": "legacy_brand_assets_rejected" + }, + { + "path": "hummingbird/androidPatientApp/app/src/main/res/mipmap-anydpi/ic_launcher.xml", + "sha256": "32967b72dfeb5ea135f45d40ebd9a662308b0848311671dfb8b1179f531d4ac9", + "surface": "legacy_android_resource", + "classification": "rejected_legacy_behavior", + "disposition": "reject", + "categories": [ + "accessibility_presentation", + "release_packaging_identity" + ], + "decision_id": "legacy_brand_assets_rejected" + }, + { + "path": "hummingbird/androidPatientApp/app/src/main/res/mipmap-anydpi/ic_launcher_round.xml", + "sha256": "32967b72dfeb5ea135f45d40ebd9a662308b0848311671dfb8b1179f531d4ac9", + "surface": "legacy_android_resource", + "classification": "rejected_legacy_behavior", + "disposition": "reject", + "categories": [ + "accessibility_presentation", + "release_packaging_identity" + ], + "decision_id": "legacy_brand_assets_rejected" + }, + { + "path": "hummingbird/androidPatientApp/app/src/main/res/values-v27/themes.xml", + "sha256": "0127bae96a6b336f0b74c3fdef9a55cb16f4734c10cbd98ab0a65152ed4c652f", + "surface": "legacy_android_resource", + "classification": "rejected_legacy_behavior", + "disposition": "reject", + "categories": [ + "accessibility_presentation", + "release_packaging_identity" + ], + "decision_id": "legacy_brand_assets_rejected" + }, + { + "path": "hummingbird/androidPatientApp/app/src/main/res/values/colors.xml", + "sha256": "b1607d62847949a6d19c79af3f211f84b23d551a284704ed7f8a0769b06f0cd6", + "surface": "legacy_android_resource", + "classification": "rejected_legacy_behavior", + "disposition": "reject", + "categories": [ + "accessibility_presentation", + "release_packaging_identity" + ], + "decision_id": "legacy_brand_assets_rejected" + }, + { + "path": "hummingbird/androidPatientApp/app/src/main/res/values/strings.xml", + "sha256": "b1d7d59056988d2e8fd9d21cea4bad34372802a4b94d5410398c5d1d88308f42", + "surface": "legacy_android_resource", + "classification": "rejected_legacy_behavior", + "disposition": "reject", + "categories": [ + "accessibility_presentation", + "release_packaging_identity" + ], + "decision_id": "legacy_brand_assets_rejected" + }, + { + "path": "hummingbird/androidPatientApp/app/src/main/res/values/themes.xml", + "sha256": "07c20fa8e9727a16f68cfe4fb5dbf6f9ee2e14978d3b0c3eba50c9838881056a", + "surface": "legacy_android_resource", + "classification": "rejected_legacy_behavior", + "disposition": "reject", + "categories": [ + "accessibility_presentation", + "release_packaging_identity" + ], + "decision_id": "legacy_brand_assets_rejected" + }, + { + "path": "hummingbird/androidPatientApp/app/src/main/res/xml/data_extraction_rules.xml", + "sha256": "8b55ff5489071bb19a4e89967b604a16c0e4d1e87a2df3c73c64417314a59f22", + "surface": "legacy_android_resource", + "classification": "reusable_safety_primitive", + "disposition": "principle_only", + "categories": [ + "accessibility_presentation", + "governance_audit_persistence", + "release_packaging_identity" + ], + "decision_id": "native_privacy_configuration_principle" + }, + { + "path": "hummingbird/androidPatientApp/app/src/main/res/xml/network_security_config.xml", + "sha256": "dbc726438a7e99c69fdfc39146cd51ef8300dacb236843bbce3ecd2a1712a80f", + "surface": "legacy_android_resource", + "classification": "reusable_safety_primitive", + "disposition": "principle_only", + "categories": [ + "accessibility_presentation", + "governance_audit_persistence", + "release_packaging_identity" + ], + "decision_id": "native_privacy_configuration_principle" + }, + { + "path": "hummingbird/androidPatientApp/app/src/release/java/net/acumenus/hummingbird/patient/PatientLaunchHooks.kt", + "sha256": "ac8caec30b2f2b1a7b4ea4bc8cb50af6ca9666e9212a6c68180ef223ae3e50cc", + "surface": "legacy_android_release_source", + "classification": "reusable_safety_primitive", + "disposition": "principle_only", + "categories": [ + "release_packaging_identity", + "synthetic_debug" + ], + "decision_id": "release_synthetic_exclusion_principle" + }, + { + "path": "hummingbird/androidPatientApp/app/src/release/java/net/acumenus/hummingbird/patient/SyntheticReferencePatientScenario.kt", + "sha256": "92ecf57529dde3c5e538d492cd9e2a8920116d44c00034e0254ea15a23234936", + "surface": "legacy_android_release_source", + "classification": "reusable_safety_primitive", + "disposition": "principle_only", + "categories": [ + "release_packaging_identity", + "synthetic_debug" + ], + "decision_id": "release_synthetic_exclusion_principle" + }, + { + "path": "hummingbird/androidPatientApp/app/src/test/java/net/acumenus/hummingbird/patient/PatientPrivacyPolicyTest.kt", + "sha256": "56bd5ed8f45be5e8bb7cc674398b4f8fe170d963a894b94a96da810cf8fd1763", + "surface": "legacy_android_test", + "classification": "test_fixture_only", + "disposition": "test_only", + "categories": [ + "governance_audit_persistence", + "release_packaging_identity", + "test_fixture_evidence" + ], + "decision_id": "test_fixture_evidence" + }, + { + "path": "hummingbird/androidPatientApp/app/src/test/java/net/acumenus/hummingbird/patient/data/PatientSecureStoragePolicyTest.kt", + "sha256": "0cd5c6c5e8128d090858e8d4dab86abc9197187746c57e65c171e420ac19624a", + "surface": "legacy_android_test", + "classification": "test_fixture_only", + "disposition": "test_only", + "categories": [ + "governance_audit_persistence", + "release_packaging_identity", + "test_fixture_evidence" + ], + "decision_id": "test_fixture_evidence" + }, + { + "path": "hummingbird/androidPatientApp/app/src/test/java/net/acumenus/hummingbird/patient/data/PatientSessionManagementDecoderTest.kt", + "sha256": "bc6021dbf52dcd566128da2fd28a593cdb45f1c6b3f9b12f0f7552f2c30e6dcd", + "surface": "legacy_android_test", + "classification": "test_fixture_only", + "disposition": "test_only", + "categories": [ + "governance_audit_persistence", + "release_packaging_identity", + "test_fixture_evidence" + ], + "decision_id": "test_fixture_evidence" + }, + { + "path": "hummingbird/androidPatientApp/app/src/test/java/net/acumenus/hummingbird/patient/data/PatientStateVocabularyTest.kt", + "sha256": "2860937fd260f9b420cf1d2c5691bd41db34dc118907ffb8d519965a36b816c0", + "surface": "legacy_android_test", + "classification": "test_fixture_only", + "disposition": "test_only", + "categories": [ + "journey_projection_content", + "test_fixture_evidence" + ], + "decision_id": "test_fixture_evidence" + }, + { + "path": "hummingbird/androidPatientApp/app/src/test/java/net/acumenus/hummingbird/patient/ui/PatientPresentationAccessibilityTest.kt", + "sha256": "90708582a569d04fddfcc6ce4e9ab09d748b5d1e5964666c13512dc3176acbc6", + "surface": "legacy_android_test", + "classification": "test_fixture_only", + "disposition": "test_only", + "categories": [ + "accessibility_presentation", + "account_preferences", + "test_fixture_evidence" + ], + "decision_id": "test_fixture_evidence" + }, + { + "path": "hummingbird/androidPatientApp/app/src/test/java/net/acumenus/hummingbird/patient/ui/PatientVisualAssetPolicyTest.kt", + "sha256": "450fda81e232a322a12defef02b65866429c72529bbe577d9aa62b31e2c7a9c3", + "surface": "legacy_android_test", + "classification": "test_fixture_only", + "disposition": "test_only", + "categories": [ + "test_fixture_evidence" + ], + "decision_id": "test_fixture_evidence" + }, + { + "path": "hummingbird/androidPatientApp/app/src/testDebug/java/net/acumenus/hummingbird/patient/PatientLaunchHooksTest.kt", + "sha256": "044e744ad9571f3070c6173af8287787bb928e1604f41054bebd8594f58aaf34", + "surface": "legacy_android_test", + "classification": "test_fixture_only", + "disposition": "test_only", + "categories": [ + "synthetic_debug", + "test_fixture_evidence" + ], + "decision_id": "test_fixture_evidence" + }, + { + "path": "hummingbird/androidPatientApp/app/src/testDebug/java/net/acumenus/hummingbird/patient/SyntheticReferencePatientScenarioTest.kt", + "sha256": "b45fdc24f23af70bfa03148535c938f7ff54a5220142a539391b430871e10410", + "surface": "legacy_android_test", + "classification": "test_fixture_only", + "disposition": "test_only", + "categories": [ + "synthetic_debug", + "test_fixture_evidence" + ], + "decision_id": "test_fixture_evidence" + }, + { + "path": "hummingbird/androidPatientApp/app/src/testRelease/java/net/acumenus/hummingbird/patient/PatientLaunchHooksReleaseTest.kt", + "sha256": "548d1b67f1ea8a09d4a3ee180d4fee246a89c2eafb08169b31396dc2df836ae1", + "surface": "legacy_android_test", + "classification": "test_fixture_only", + "disposition": "test_only", + "categories": [ + "governance_audit_persistence", + "release_packaging_identity", + "synthetic_debug", + "test_fixture_evidence" + ], + "decision_id": "test_fixture_evidence" + }, + { + "path": "hummingbird/androidPatientApp/build.gradle.kts", + "sha256": "a2c1a3dc7e80664eeede26bfe0b554c1afbbfe81b9b5dfbc33e6aa3fcee3ee1e", + "surface": "legacy_android_packaging", + "classification": "rejected_legacy_behavior", + "disposition": "reject", + "categories": [ + "release_packaging_identity" + ], + "decision_id": "legacy_packaging_identity_rejected" + }, + { + "path": "hummingbird/androidPatientApp/gradle.properties", + "sha256": "9d5b21faa6040ab2b6ccfe991f8132c7b11851d6ca4e55a7a6f58f71a04c44ee", + "surface": "legacy_android_packaging", + "classification": "rejected_legacy_behavior", + "disposition": "reject", + "categories": [ + "release_packaging_identity" + ], + "decision_id": "legacy_packaging_identity_rejected" + }, + { + "path": "hummingbird/androidPatientApp/gradle/wrapper/gradle-wrapper.jar", + "sha256": "497c8c2a7e5031f6aa847f88104aa80a93532ec32ee17bdb8d1d2f67a194a9c7", + "surface": "legacy_android_packaging", + "classification": "rejected_legacy_behavior", + "disposition": "reject", + "categories": [ + "release_packaging_identity" + ], + "decision_id": "legacy_packaging_identity_rejected" + }, + { + "path": "hummingbird/androidPatientApp/gradle/wrapper/gradle-wrapper.properties", + "sha256": "ad8aae83783da405b1acb1d39069787f8de5fd64ae5dbf1480f8ced80956c433", + "surface": "legacy_android_packaging", + "classification": "rejected_legacy_behavior", + "disposition": "reject", + "categories": [ + "release_packaging_identity" + ], + "decision_id": "legacy_packaging_identity_rejected" + }, + { + "path": "hummingbird/androidPatientApp/gradlew", + "sha256": "a5a5c199ba02189ae8c46a334223371a20599d9c298ef65e7540ede4a3f72d59", + "surface": "legacy_android_packaging", + "classification": "rejected_legacy_behavior", + "disposition": "reject", + "categories": [ + "release_packaging_identity" + ], + "decision_id": "legacy_packaging_identity_rejected" + }, + { + "path": "hummingbird/androidPatientApp/gradlew.bat", + "sha256": "59328c7a17f673b1a63040bfb380a0c749e5d6df3406f7f18641060314cd9aa1", + "surface": "legacy_android_packaging", + "classification": "rejected_legacy_behavior", + "disposition": "reject", + "categories": [ + "release_packaging_identity" + ], + "decision_id": "legacy_packaging_identity_rejected" + }, + { + "path": "hummingbird/androidPatientApp/settings.gradle.kts", + "sha256": "6704f6599b085bf9f5856113606e0dc90d1791c33eb549243bf6c8f0b22154b2", + "surface": "legacy_android_packaging", + "classification": "rejected_legacy_behavior", + "disposition": "reject", + "categories": [ + "release_packaging_identity" + ], + "decision_id": "legacy_packaging_identity_rejected" + }, + { + "path": "hummingbird/iosPatientApp/.gitignore", + "sha256": "a1261f533e260aed632664278122db5047ba7ab942023a2f4cbcbb510608e65f", + "surface": "legacy_ios_packaging", + "classification": "reusable_safety_primitive", + "disposition": "principle_only", + "categories": [ + "release_packaging_identity" + ], + "decision_id": "generated_artifact_hygiene_principle" + }, + { + "path": "hummingbird/iosPatientApp/HummingbirdPatient.xcodeproj/project.pbxproj", + "sha256": "66ea4fa9cd21fe2a983875df59c7ed7037f44dbde95e07e721acc4321a7a5fbc", + "surface": "legacy_ios_packaging", + "classification": "rejected_legacy_behavior", + "disposition": "reject", + "categories": [ + "release_packaging_identity" + ], + "decision_id": "legacy_packaging_identity_rejected" + }, + { + "path": "hummingbird/iosPatientApp/HummingbirdPatient.xcodeproj/project.xcworkspace/contents.xcworkspacedata", + "sha256": "7f3b00b5c3fdb45242d7b87e1e5c4e25d1fa8129a16c94295ecc4e8ea2235c5f", + "surface": "legacy_ios_packaging", + "classification": "rejected_legacy_behavior", + "disposition": "reject", + "categories": [ + "release_packaging_identity" + ], + "decision_id": "legacy_packaging_identity_rejected" + }, + { + "path": "hummingbird/iosPatientApp/HummingbirdPatient.xcodeproj/xcshareddata/xcschemes/HummingbirdPatient.xcscheme", + "sha256": "47231cc1d46e3c93451fedbbd52ab0bde09cf3e949fbe4180c3eabf087f9d8ab", + "surface": "legacy_ios_packaging", + "classification": "rejected_legacy_behavior", + "disposition": "reject", + "categories": [ + "release_packaging_identity" + ], + "decision_id": "legacy_packaging_identity_rejected" + }, + { + "path": "hummingbird/iosPatientApp/HummingbirdPatient/App/HummingbirdPatientApp.swift", + "sha256": "8b9cc69d7a5fd78405ac0951d65d8d15673d3c6b548d926607626102679e538b", + "surface": "legacy_ios_source", + "classification": "reusable_product_behavior", + "disposition": "held", + "categories": [ + "accessibility_presentation", + "journey_projection_content" + ], + "decision_id": "native_journey_behavior_held" + }, + { + "path": "hummingbird/iosPatientApp/HummingbirdPatient/App/PatientAppConfiguration.swift", + "sha256": "e36f60e49f7260606af177d89a0c52a58f947a2ee8abe104994b990638957c64", + "surface": "legacy_ios_source", + "classification": "rejected_legacy_behavior", + "disposition": "reject", + "categories": [ + "release_packaging_identity" + ], + "decision_id": "legacy_runtime_activation_rejected" + }, + { + "path": "hummingbird/iosPatientApp/HummingbirdPatient/App/PatientRootView.swift", + "sha256": "a2a8a51af7378a70d9724c4471a2b5d5dd17fa2a658fea085e644ab9b734c0c4", + "surface": "legacy_ios_source", + "classification": "reusable_product_behavior", + "disposition": "held", + "categories": [ + "accessibility_presentation", + "journey_projection_content" + ], + "decision_id": "native_journey_behavior_held" + }, + { + "path": "hummingbird/iosPatientApp/HummingbirdPatient/Assets.xcassets/AppIcon.appiconset/Contents.json", + "sha256": "87b9bc128c3684b599a2054929029b11607709101b666bbfc09b3f96c97cbbb5", + "surface": "legacy_ios_asset", + "classification": "rejected_legacy_behavior", + "disposition": "reject", + "categories": [ + "accessibility_presentation", + "release_packaging_identity" + ], + "decision_id": "legacy_brand_assets_rejected" + }, + { + "path": "hummingbird/iosPatientApp/HummingbirdPatient/Assets.xcassets/AppIcon.appiconset/icon-1024.png", + "sha256": "870057ce4e265f4899fccaf1585e915aed04a6afa9920bdb7140250bba820cf6", + "surface": "legacy_ios_asset", + "classification": "rejected_legacy_behavior", + "disposition": "reject", + "categories": [ + "accessibility_presentation", + "release_packaging_identity" + ], + "decision_id": "legacy_brand_assets_rejected" + }, + { + "path": "hummingbird/iosPatientApp/HummingbirdPatient/Assets.xcassets/Contents.json", + "sha256": "0fd49ba3c3585c709678e0046a821c3c60685ec7063720d30d3a3448be3a208b", + "surface": "legacy_ios_asset", + "classification": "rejected_legacy_behavior", + "disposition": "reject", + "categories": [ + "accessibility_presentation", + "release_packaging_identity" + ], + "decision_id": "legacy_brand_assets_rejected" + }, + { + "path": "hummingbird/iosPatientApp/HummingbirdPatient/Assets.xcassets/PatientAiryFlight.imageset/Contents.json", + "sha256": "d8ca9313109a826550e715222f159e6323b4d6d612ac09f55f6312eddf51a7d8", + "surface": "legacy_ios_asset", + "classification": "rejected_legacy_behavior", + "disposition": "reject", + "categories": [ + "accessibility_presentation", + "release_packaging_identity" + ], + "decision_id": "legacy_brand_assets_rejected" + }, + { + "path": "hummingbird/iosPatientApp/HummingbirdPatient/Assets.xcassets/PatientAiryFlight.imageset/hummingbird-06.jpg", + "sha256": "65b640f035d8527d879f5b354e36eabb78ec3c9ca5c915356e05912a4aadc008", + "surface": "legacy_ios_asset", + "classification": "rejected_legacy_behavior", + "disposition": "reject", + "categories": [ + "accessibility_presentation", + "release_packaging_identity" + ], + "decision_id": "legacy_brand_assets_rejected" + }, + { + "path": "hummingbird/iosPatientApp/HummingbirdPatient/Assets.xcassets/PatientCalmGreen.imageset/Contents.json", + "sha256": "7542e76834b5d5072f1a5021e74bae913752b441e836afe5fa4b7ef13a1f20b5", + "surface": "legacy_ios_asset", + "classification": "rejected_legacy_behavior", + "disposition": "reject", + "categories": [ + "accessibility_presentation", + "release_packaging_identity" + ], + "decision_id": "legacy_brand_assets_rejected" + }, + { + "path": "hummingbird/iosPatientApp/HummingbirdPatient/Assets.xcassets/PatientCalmGreen.imageset/hummingbird-01.jpg", + "sha256": "9230a368fd2c0cab308280425b35b645b2a505871277a8136ec1c199cd53d6dc", + "surface": "legacy_ios_asset", + "classification": "rejected_legacy_behavior", + "disposition": "reject", + "categories": [ + "accessibility_presentation", + "release_packaging_identity" + ], + "decision_id": "legacy_brand_assets_rejected" + }, + { + "path": "hummingbird/iosPatientApp/HummingbirdPatient/Assets.xcassets/PatientCareConnection.imageset/Contents.json", + "sha256": "6272f92fb2cc5346c7a949692f0452367bfbae1499bf008ff0b1f332e949e91f", + "surface": "legacy_ios_asset", + "classification": "rejected_legacy_behavior", + "disposition": "reject", + "categories": [ + "accessibility_presentation", + "release_packaging_identity" + ], + "decision_id": "legacy_brand_assets_rejected" + }, + { + "path": "hummingbird/iosPatientApp/HummingbirdPatient/Assets.xcassets/PatientCareConnection.imageset/hummingbird-07.jpg", + "sha256": "b545c010ed87c9ee4150c616b6031f2e66b329bc0e339fbe4ac735d6f4236988", + "surface": "legacy_ios_asset", + "classification": "rejected_legacy_behavior", + "disposition": "reject", + "categories": [ + "accessibility_presentation", + "release_packaging_identity" + ], + "decision_id": "legacy_brand_assets_rejected" + }, + { + "path": "hummingbird/iosPatientApp/HummingbirdPatient/Assets.xcassets/PatientWarmMotion.imageset/Contents.json", + "sha256": "6a0a0c19d4c46d8e7969aa0e2513401cdad1ca0ef141b54b23743dcab7b57f91", + "surface": "legacy_ios_asset", + "classification": "rejected_legacy_behavior", + "disposition": "reject", + "categories": [ + "accessibility_presentation", + "release_packaging_identity" + ], + "decision_id": "legacy_brand_assets_rejected" + }, + { + "path": "hummingbird/iosPatientApp/HummingbirdPatient/Assets.xcassets/PatientWarmMotion.imageset/hummingbird-12.jpg", + "sha256": "38e37231c4a14e3223823bbee531590aadd982bc7994c6538b02d291670b729d", + "surface": "legacy_ios_asset", + "classification": "rejected_legacy_behavior", + "disposition": "reject", + "categories": [ + "accessibility_presentation", + "release_packaging_identity" + ], + "decision_id": "legacy_brand_assets_rejected" + }, + { + "path": "hummingbird/iosPatientApp/HummingbirdPatient/DesignSystem/PatientComponents.swift", + "sha256": "6eb232a2d02903f4c02a823dc3fb2537f756fe5b0c971813920e4544eb520d86", + "surface": "legacy_ios_source", + "classification": "reusable_safety_primitive", + "disposition": "principle_only", + "categories": [ + "accessibility_presentation", + "account_preferences" + ], + "decision_id": "native_presentation_safety_principle" + }, + { + "path": "hummingbird/iosPatientApp/HummingbirdPatient/DesignSystem/PatientPhotoBackground.swift", + "sha256": "a4dafccaa89c202261f306d8c746d3af0fd4477a45d2ad832ed063a792f4c515", + "surface": "legacy_ios_source", + "classification": "reusable_safety_primitive", + "disposition": "principle_only", + "categories": [ + "accessibility_presentation", + "account_preferences" + ], + "decision_id": "native_presentation_safety_principle" + }, + { + "path": "hummingbird/iosPatientApp/HummingbirdPatient/DesignSystem/PatientPresentationPreferences.swift", + "sha256": "8f94ec1de27c991e6cd52808d60fb361d96570596196b9cb3b15951a84f89a9f", + "surface": "legacy_ios_source", + "classification": "reusable_safety_primitive", + "disposition": "principle_only", + "categories": [ + "accessibility_presentation", + "account_preferences" + ], + "decision_id": "native_presentation_safety_principle" + }, + { + "path": "hummingbird/iosPatientApp/HummingbirdPatient/Features/Account/PatientPreferencesView.swift", + "sha256": "3e001e47c8b7d12120ba6217d2b84ef4ccc3877ff3cbbdc2b8b4b102f678d185", + "surface": "legacy_ios_source", + "classification": "reusable_product_behavior", + "disposition": "held", + "categories": [ + "accessibility_presentation", + "journey_projection_content" + ], + "decision_id": "native_journey_behavior_held" + }, + { + "path": "hummingbird/iosPatientApp/HummingbirdPatient/Features/Account/PatientSessionManagementView.swift", + "sha256": "af8c9135fb45fac2ce6433419144fb463b0e966396b0dba16455ef460036575e", + "surface": "legacy_ios_source", + "classification": "reusable_product_behavior", + "disposition": "held", + "categories": [ + "accessibility_presentation", + "journey_projection_content" + ], + "decision_id": "native_journey_behavior_held" + }, + { + "path": "hummingbird/iosPatientApp/HummingbirdPatient/Features/CareTeam/PatientCareTeamView.swift", + "sha256": "c7dd9828210960b3e2ec70ed2d916d92f2a66f08be920676e7f4318732f04694", + "surface": "legacy_ios_source", + "classification": "reusable_product_behavior", + "disposition": "held", + "categories": [ + "accessibility_presentation", + "journey_projection_content" + ], + "decision_id": "native_journey_behavior_held" + }, + { + "path": "hummingbird/iosPatientApp/HummingbirdPatient/Features/Shell/PatientTabShellView.swift", + "sha256": "d2fb058bbabc2184962a48cb948b70848b96c07d2eebbf0140ee9820fd3653e3", + "surface": "legacy_ios_source", + "classification": "reusable_product_behavior", + "disposition": "held", + "categories": [ + "accessibility_presentation", + "journey_projection_content" + ], + "decision_id": "native_journey_behavior_held" + }, + { + "path": "hummingbird/iosPatientApp/HummingbirdPatient/Features/Today/PatientTodayView.swift", + "sha256": "63f2003a80cc316f4f47d76cc0a1e56b2d3c77875004be78c27d252d0fcae663", + "surface": "legacy_ios_source", + "classification": "reusable_product_behavior", + "disposition": "held", + "categories": [ + "accessibility_presentation", + "journey_projection_content" + ], + "decision_id": "native_journey_behavior_held" + }, + { + "path": "hummingbird/iosPatientApp/HummingbirdPatient/Info.plist", + "sha256": "ef085850aa7cf6ef7d462e94ac06f9bda4999ff80aa098205ad6f896c5099c16", + "surface": "legacy_ios_packaging", + "classification": "rejected_legacy_behavior", + "disposition": "reject", + "categories": [ + "release_packaging_identity" + ], + "decision_id": "legacy_packaging_identity_rejected" + }, + { + "path": "hummingbird/iosPatientApp/HummingbirdPatient/Privacy/PatientPrivacyCoverView.swift", + "sha256": "bb16dcf8c61a281429f4b4936539ecd455c70453cdd80f3520b25f831ec3cad4", + "surface": "legacy_ios_source", + "classification": "reusable_safety_primitive", + "disposition": "principle_only", + "categories": [ + "accessibility_presentation", + "account_preferences" + ], + "decision_id": "native_presentation_safety_principle" + }, + { + "path": "hummingbird/iosPatientApp/HummingbirdPatientTests/PatientPresentationPreferencesTests.swift", + "sha256": "7c0048897a8262273a22e7576b774f98537fb000233ec49b99c042ccc9f5cf76", + "surface": "legacy_ios_test", + "classification": "test_fixture_only", + "disposition": "test_only", + "categories": [ + "accessibility_presentation", + "account_preferences", + "test_fixture_evidence" + ], + "decision_id": "test_fixture_evidence" + }, + { + "path": "hummingbird/iosPatientApp/HummingbirdPatientTests/PatientStateVocabularyTests.swift", + "sha256": "cf781feea9a3fe4f3befaaddd58253747abb17d70a6d4fc60d0df52c00938069", + "surface": "legacy_ios_test", + "classification": "test_fixture_only", + "disposition": "test_only", + "categories": [ + "journey_projection_content", + "test_fixture_evidence" + ], + "decision_id": "test_fixture_evidence" + }, + { + "path": "hummingbird/iosPatientApp/HummingbirdPatientUITests/PatientSessionManagementUITests.swift", + "sha256": "27e955f9aae983bfc082e53e98a3b99194755faec4f8c625a09c859f09e87429", + "surface": "legacy_ios_ui_test", + "classification": "test_fixture_only", + "disposition": "test_only", + "categories": [ + "governance_audit_persistence", + "release_packaging_identity", + "test_fixture_evidence" + ], + "decision_id": "test_fixture_evidence" + }, + { + "path": "hummingbird/iosPatientApp/project.yml", + "sha256": "9e10eeac89e49bf9ae4dd8aabdaf30e2fcddb20fc827bda006e35e1d9c7321ba", + "surface": "legacy_ios_packaging", + "classification": "rejected_legacy_behavior", + "disposition": "reject", + "categories": [ + "release_packaging_identity" + ], + "decision_id": "legacy_packaging_identity_rejected" + }, + { + "path": "tests/Feature/Patient/HummingbirdPatientReferenceIdentityProvisionerTest.php", + "sha256": "8826f2e9c3d2270fbcbcd2aeb7b6d30584da1264b26741525a7371f6f6831d03", + "surface": "legacy_backend_test", + "classification": "test_fixture_only", + "disposition": "test_only", + "categories": [ + "synthetic_debug", + "test_fixture_evidence" + ], + "decision_id": "test_fixture_evidence" + }, + { + "path": "tests/Feature/Patient/HummingbirdPatientReferenceProjectionDraftProvisionerTest.php", + "sha256": "5a541cc157d8619865864b15e939981083fe408381739fdc7da821bfcd1ce9bf", + "surface": "legacy_backend_test", + "classification": "test_fixture_only", + "disposition": "test_only", + "categories": [ + "synthetic_debug", + "test_fixture_evidence" + ], + "decision_id": "test_fixture_evidence" + }, + { + "path": "tests/Feature/Patient/PatientAuthoredGoalApiTest.php", + "sha256": "f9422521a2a4f432a18ad75dddba503e967ac27c05e88350a63bc084d1b978f4", + "surface": "legacy_backend_test", + "classification": "test_fixture_only", + "disposition": "test_only", + "categories": [ + "governance_audit_persistence", + "release_packaging_identity", + "test_fixture_evidence" + ], + "decision_id": "test_fixture_evidence" + }, + { + "path": "tests/Feature/Patient/PatientCarePreferenceApiTest.php", + "sha256": "cb1363bc46a219fa20d2f3dac11adaaf9970d413e85d2eda1004847f4d9bb888", + "surface": "legacy_backend_test", + "classification": "test_fixture_only", + "disposition": "test_only", + "categories": [ + "accessibility_presentation", + "account_preferences", + "test_fixture_evidence" + ], + "decision_id": "test_fixture_evidence" + }, + { + "path": "tests/Feature/Patient/PatientPathwayHistoryDraftCommandTest.php", + "sha256": "3aee3c88ef221f62dce85699a1cfe4dff11b8455a4aff78c2925064ef496a98e", + "surface": "legacy_backend_test", + "classification": "test_fixture_only", + "disposition": "test_only", + "categories": [ + "journey_projection_content", + "test_fixture_evidence" + ], + "decision_id": "test_fixture_evidence" + }, + { + "path": "tests/Feature/Patient/PatientPathwayInstanceHistoryTest.php", + "sha256": "465f9c0884329eb8171aac66feb3da5d360112d3d41acfc6ef132e8fc7c9f35e", + "surface": "legacy_backend_test", + "classification": "test_fixture_only", + "disposition": "test_only", + "categories": [ + "journey_projection_content", + "test_fixture_evidence" + ], + "decision_id": "test_fixture_evidence" + }, + { + "path": "tests/Feature/Patient/PatientPathwayProjectionReviewReleaseTest.php", + "sha256": "d81c747f9fb56c420ea7fe0a7adffc2afeb6766711ca5ac385bb94892179ad29", + "surface": "legacy_backend_test", + "classification": "test_fixture_only", + "disposition": "test_only", + "categories": [ + "governance_audit_persistence", + "journey_projection_content", + "release_packaging_identity", + "test_fixture_evidence" + ], + "decision_id": "test_fixture_evidence" + }, + { + "path": "tests/Feature/Patient/PatientPathwaySourceReconciliationTest.php", + "sha256": "4adb4908d28f2e27d4255cc9c208f5231773d641d1d5438b0fdbede9c7a4d8a0", + "surface": "legacy_backend_test", + "classification": "test_fixture_only", + "disposition": "test_only", + "categories": [ + "journey_projection_content", + "test_fixture_evidence" + ], + "decision_id": "test_fixture_evidence" + }, + { + "path": "tests/Unit/Patient/PatientProjectionStateVocabularyTest.php", + "sha256": "67fa6e822ccf0dd40c3128d8368e9dcb4e88ce5e7b7b3c63cbcc76f8413df77b", + "surface": "legacy_backend_test", + "classification": "test_fixture_only", + "disposition": "test_only", + "categories": [ + "journey_projection_content", + "test_fixture_evidence" + ], + "decision_id": "test_fixture_evidence" + } + ] +} diff --git a/docs/nightingale/migration/candidates/v0/source-classification.json b/docs/nightingale/migration/candidates/v0/source-classification.json new file mode 100644 index 00000000..149d02f1 --- /dev/null +++ b/docs/nightingale/migration/candidates/v0/source-classification.json @@ -0,0 +1,747 @@ +{ + "schema_version": 1, + "classification_id": "nightingale-source-classification.v1", + "reviewed_source_commit": "b1078405de2dacd767ec69da11197f1e755d8277", + "reviewed_at": "2026-07-26", + "status": "evidence_only_not_approved_for_implementation", + "scope": [ + "identity_input", + "enrollment_recovery", + "first_read_projection", + "error_non_disclosure" + ], + "global_constraints": { + "implementation_permitted": false, + "runtime_adoption_permitted": false, + "route_registration_permitted": false, + "legacy_route_alias_permitted": false, + "legacy_identity_provider_reuse_permitted": false, + "legacy_credential_migration_permitted": false, + "legacy_device_identity_reuse_permitted": false, + "first_record_selection_permitted": false, + "server_message_passthrough_permitted": false, + "projection_absence_conflation_permitted": false, + "production_data_used": false, + "production_query_permitted": false, + "production_replay_permitted": false, + "patient_or_principal_created": false + }, + "dispositions": { + "reimplement_principle_only": "A bounded safety property may inform a new Nightingale-owned design, but no code, identifier, route, schema, credential, copy, or product policy is approved for reuse.", + "evidence_only": "The source or test is retained as evidence about current behavior and coverage. It is not a Nightingale requirement and cannot authorize implementation.", + "held": "The behavior depends on unresolved ownership, assurance, clinical, privacy, source, contract, or patient-language decisions.", + "reject": "The behavior is incompatible with the independent Nightingale boundary and must not be migrated." + }, + "sources": [ + { + "path": "docs/hummingbird/api-contract/hummingbird-patient.v1.yaml", + "sha256": "fb6220b4ef8eb106223624a9785256fdc1603995f281f430a79897905cb45a1b", + "surface": "legacy_contract", + "categories": [ + "identity_input", + "enrollment_recovery", + "first_read_projection", + "error_non_disclosure" + ], + "disposition": "evidence_only", + "decision": "Inventory request, response, scope, and error shapes only; do not copy paths, operation identifiers, security schemes, or Hummingbird schemas into Nightingale." + }, + { + "path": "routes/patient.php", + "sha256": "e892a9d570668bec6ae54d18cbfd891f7d9df2e2c2f8f6e3689125d6b465af28", + "surface": "legacy_backend", + "categories": [ + "identity_input", + "enrollment_recovery", + "first_read_projection", + "error_non_disclosure" + ], + "disposition": "reject", + "decision": "Reject every legacy path, name, middleware composition, feature flag, token ability, and route alias; retain only the principle of explicit layered gates." + }, + { + "path": "config/hummingbird-patient.php", + "sha256": "d5dafe054077892e649fd44f95edc6bc1bb499e4dffeb4f4390316823db07625", + "surface": "legacy_backend", + "categories": [ + "identity_input", + "enrollment_recovery", + "first_read_projection", + "error_non_disclosure" + ], + "disposition": "reject", + "decision": "Reject Hummingbird flags, token lifetimes, policy names, topic copy, source keys, and environment-controlled activation; use only as a hazard and control inventory." + }, + { + "path": "database/migrations/2026_07_19_000100_create_patient_experience_identity_foundation.php", + "sha256": "f093d43a7a84b00ee519f6b1392a8aee5018ab1f43d930ea5abf1c4d7ecaa1e2", + "surface": "legacy_database", + "categories": [ + "identity_input", + "enrollment_recovery", + "error_non_disclosure" + ], + "disposition": "held", + "decision": "Schema constraints, hashed challenges, encrypted linkage, effective grants, isolated sessions, and append-only audit are candidate principles; every table, purpose, status, relationship, credential, and source binding remains unapproved." + }, + { + "path": "database/migrations/2026_07_19_000200_create_patient_experience_projection_kernel.php", + "sha256": "20c55f5d563bfd3342ec4e833d71dd32b4f04c49fe1c455a96b05a03aae257ef", + "surface": "legacy_database", + "categories": ["first_read_projection", "error_non_disclosure"], + "disposition": "held", + "decision": "Versioned release, provenance, uncertainty, correction, and append-only projection concepts require a Nightingale-owned schema and named approvals before reuse." + }, + { + "path": "app/Http/Concerns/RendersPatientEnvelope.php", + "sha256": "d42daaf79047cd4ba8ebb971ec883a9cb1b3da5a648537016e193bb210c93fbb", + "surface": "legacy_backend", + "categories": ["first_read_projection", "error_non_disclosure"], + "disposition": "reimplement_principle_only", + "decision": "Reimplement an exact Nightingale envelope and no-store policy only after contract approval; reject direct trait reuse and legacy metadata ownership." + }, + { + "path": "app/Http/Controllers/Api/Patient/AuthController.php", + "sha256": "2e53bd85515c7eabf476724574a0035f5c77236dcdae8fb5d57e61ea016eb6c7", + "surface": "legacy_backend", + "categories": [ + "identity_input", + "enrollment_recovery", + "error_non_disclosure" + ], + "disposition": "held", + "decision": "Controller flow is evidence for bounded auth failures and revocation, but all enrollment, password, refresh, response, and route choices remain held." + }, + { + "path": "app/Http/Controllers/Api/Patient/EncounterController.php", + "sha256": "3de0a2edeb487a890dba2166791f5c36b1ce97d9d82ec1967054d0fad251c81b", + "surface": "legacy_backend", + "categories": ["first_read_projection", "error_non_disclosure"], + "disposition": "held", + "decision": "Retain request-time authorization and durable disclosure audit as candidate principles; reject seven-field grant serialization, row-oriented audit, and grant-row freshness claims." + }, + { + "path": "app/Http/Controllers/Api/Patient/EncounterProjectionController.php", + "sha256": "8d55765655a35ea87d809c5bf964a4e8b25ecb6a25081788d05319bbbfbacaf0", + "surface": "legacy_backend", + "categories": ["first_read_projection", "error_non_disclosure"], + "disposition": "reimplement_principle_only", + "decision": "Generic not-found collapse is a candidate anti-oracle principle; all six kinds, paths, scopes, and legacy disclosure service coupling are held." + }, + { + "path": "app/Http/Controllers/Api/Patient/MeController.php", + "sha256": "d32343fc395fa540bd984e8b26e9a6f4e15daa8a99a057f0928d640d069901f3", + "surface": "legacy_backend", + "categories": ["identity_input", "error_non_disclosure"], + "disposition": "reject", + "decision": "Reject the legacy profile projection as Nightingale identity input because it exposes email, phone, verification state, principal type, locale, timezone, and raw preference structure before a Nightingale field-ownership decision." + }, + { + "path": "app/Http/Controllers/Api/Patient/SessionController.php", + "sha256": "7cb84bbd04dccce36fdd3fe4380d52911bca3c8ca33491e41babb9fb22ac8dbb", + "surface": "legacy_backend", + "categories": [ + "identity_input", + "enrollment_recovery", + "error_non_disclosure" + ], + "disposition": "reimplement_principle_only", + "decision": "Owned-session revocation and unknown/cross-principal collapse are useful principles; reject legacy session UUIDs, device metadata, auth-method labels, and route contract." + }, + { + "path": "app/Http/Requests/Patient/TokenRequest.php", + "sha256": "f1030e037737514987c0f1bd9921786652c080bf4a856fc7fd54fc1625680aac", + "surface": "legacy_backend", + "categories": ["identity_input", "error_non_disclosure"], + "disposition": "reject", + "decision": "Reject email/password and optional durable-device input as a Nightingale provider decision; validation limits are evidence only." + }, + { + "path": "app/Http/Requests/Patient/VerifyEnrollmentChallengeRequest.php", + "sha256": "db606dff38332eda36cef5b060479771f331f1d71a3eac83499cf5dde47c5805", + "surface": "legacy_backend", + "categories": [ + "identity_input", + "enrollment_recovery", + "error_non_disclosure" + ], + "disposition": "held", + "decision": "Two-part proof, bounded lengths, and password confirmation are evidence; challenge shape, local account creation, password policy, device fields, and representative behavior require an approved proofing design." + }, + { + "path": "app/Http/Middleware/EnsureHummingbirdPatientEnabled.php", + "sha256": "0fa5ff4f732b171873450e66f377fd03b51ae5231a7b21464160e999d82b6b9c", + "surface": "legacy_backend", + "categories": [ + "identity_input", + "first_read_projection", + "error_non_disclosure" + ], + "disposition": "reimplement_principle_only", + "decision": "Default-off product gating is required in principle; reject the Hummingbird flag and middleware as Nightingale authority." + }, + { + "path": "app/Http/Middleware/EnsureHummingbirdPatientFeatureEnabled.php", + "sha256": "087dca7053d8199974589fd81fe88e1e03ff8da54f0ce6ac6305b7014f422b07", + "surface": "legacy_backend", + "categories": [ + "identity_input", + "enrollment_recovery", + "first_read_projection", + "error_non_disclosure" + ], + "disposition": "reimplement_principle_only", + "decision": "Per-operation fail-closed activation is a required principle; reject arbitrary legacy feature names and Hummingbird configuration ownership." + }, + { + "path": "app/Http/Middleware/EnsurePatientRealm.php", + "sha256": "0f4c513a0aeb39e8509417e7d3ed885c5a0ed6ba5d31072bd278a411af1dcc67", + "surface": "legacy_backend", + "categories": [ + "identity_input", + "enrollment_recovery", + "first_read_projection", + "error_non_disclosure" + ], + "disposition": "reimplement_principle_only", + "decision": "Exact realm, token-name, session-owner, status, and expiry verification is useful evidence; Nightingale must use an independent provider, credential format, session model, and error contract." + }, + { + "path": "app/Http/Middleware/ProtectPatientResponse.php", + "sha256": "8930258e8f14554553fc6f6f8a593ce17ba829306f8dd4acab45d6d0c2743f5e", + "surface": "legacy_backend", + "categories": ["error_non_disclosure"], + "disposition": "reimplement_principle_only", + "decision": "Central response decoration is a useful architectural primitive; Nightingale needs its own route matching, decorator, tests, and safe-error registry." + }, + { + "path": "app/Models/Patient/PatientPrincipal.php", + "sha256": "25fd0415462d4ad0e63020b40678e79328cebd92e8b9ba9d6dbf9c5052218370", + "surface": "legacy_backend", + "categories": [ + "identity_input", + "enrollment_recovery", + "error_non_disclosure" + ], + "disposition": "reject", + "decision": "Reject the local Hummingbird principal and password model as Nightingale identity; no principal row, credential, email, phone, preference, or status migrates." + }, + { + "path": "app/Models/Patient/PatientIdentityLink.php", + "sha256": "14c975603f2417769e45ccef9d7c82dd1ef9bf89955181522127d09e36645dea", + "surface": "legacy_backend", + "categories": [ + "identity_input", + "enrollment_recovery", + "first_read_projection", + "error_non_disclosure" + ], + "disposition": "held", + "decision": "Encrypted source linkage, keyed lookup, provenance, merge, and revocation are candidate properties; source authority, assurance, and linkage lifecycle are unresolved." + }, + { + "path": "app/Models/Patient/PatientEnrollmentChallenge.php", + "sha256": "85fe910b3e4b457013773b80063416798da93208322de67659b939f61d9c7e53", + "surface": "legacy_backend", + "categories": [ + "identity_input", + "enrollment_recovery", + "error_non_disclosure" + ], + "disposition": "held", + "decision": "One-way secret checks, attempt limits, expiry, consumption, and revocation are evidence; a model defaulting to encounter enrollment cannot serve as Nightingale enrollment or recovery authority." + }, + { + "path": "app/Models/Patient/PatientSession.php", + "sha256": "b18a83a4bdc92591a2bc4a220d633d84ff5c6febeb853314d0e11299ea5195d6", + "surface": "legacy_backend", + "categories": [ + "identity_input", + "enrollment_recovery", + "error_non_disclosure" + ], + "disposition": "held", + "decision": "Session-family, expiry, revocation, and ownership concepts are held evidence; reject token-family, device-fingerprint, network metadata, and legacy session migration." + }, + { + "path": "app/Models/Patient/PatientEncounterAccessGrant.php", + "sha256": "237908876e22fe2c3fcd6e55cedde87046288adb4ddced43368273acf97934e5", + "surface": "legacy_backend", + "categories": [ + "identity_input", + "first_read_projection", + "error_non_disclosure" + ], + "disposition": "held", + "decision": "Effective ownership and revocation are useful evidence; raw scopes, relationship defaults, source linkage, nullability drift, and external identifiers are not approved for Nightingale." + }, + { + "path": "app/Models/Patient/PatientEncounterProjection.php", + "sha256": "b2675fea1fa6d2a3362ce54bb732858da221604f1f388b766d8fea1f6f449eb0", + "surface": "legacy_backend", + "categories": ["first_read_projection", "error_non_disclosure"], + "disposition": "held", + "decision": "Immutable released projection concepts are held; model, kinds, content, provenance, required scopes, and release states need Nightingale ownership and clinical approval." + }, + { + "path": "app/Models/Patient/PatientReleasePolicyVersion.php", + "sha256": "2167f8b301b3fb6d259490e450bb124a1dd4e21f77e3249f5821bdba56407f63", + "surface": "legacy_backend", + "categories": ["first_read_projection", "error_non_disclosure"], + "disposition": "held", + "decision": "Effective versioned release policy is a required principle, but no Hummingbird policy row or environment-selected version is a Nightingale approval." + }, + { + "path": "app/Policies/Patient/PatientEncounterAccessGrantPolicy.php", + "sha256": "61ca77c6713c23be4e3ab522edf9f279b517aef52d5d75ebf68d5a1b9d4fd58c", + "surface": "legacy_backend", + "categories": [ + "identity_input", + "first_read_projection", + "error_non_disclosure" + ], + "disposition": "held", + "decision": "Request-time principal, relationship, state, and effective-window checks are evidence; missing identity-link and source-currentness gates prevent adoption." + }, + { + "path": "app/Policies/Patient/PatientEncounterProjectionPolicy.php", + "sha256": "582267c8e7aff6d8e68574319907654fcf3111fb93834221cfffc48943362209", + "surface": "legacy_backend", + "categories": ["first_read_projection", "error_non_disclosure"], + "disposition": "held", + "decision": "Projection policy provides defense in depth but remains coupled to Hummingbird grant, scope, relationship, release-state, and policy-version choices." + }, + { + "path": "app/Services/Patient/PatientAuthFailure.php", + "sha256": "3b5a6779f23afbcb142da0beaf6f3d208aa5c97f0ed01ea3a519a3e1b7ee6191", + "surface": "legacy_backend", + "categories": [ + "identity_input", + "enrollment_recovery", + "error_non_disclosure" + ], + "disposition": "reimplement_principle_only", + "decision": "Typed internal failure is useful, but messages must never be serialized directly; Nightingale must map owned codes to approved localized patient copy at the outer boundary." + }, + { + "path": "app/Services/Patient/PatientAuthService.php", + "sha256": "98fcfd11460870f75eb5ea7cbb6e5cbf4f45fefe2fccc0c1b5ad50f0a0bf214d", + "surface": "legacy_backend", + "categories": [ + "identity_input", + "enrollment_recovery", + "error_non_disclosure" + ], + "disposition": "held", + "decision": "Row locking, atomic challenge consumption, generic credential denial, refresh rotation/reuse response, ownership, revocation, cleanup, and audit are evidence; reject local email/password, legacy models, session/device fingerprinting, and absent recovery workflow." + }, + { + "path": "app/Services/Patient/PatientEncounterAccessService.php", + "sha256": "a593503e20dc8ba45699af4974f36345a0e85b739fa2f428565c8d3d4b079ef9", + "surface": "legacy_backend", + "categories": ["first_read_projection", "error_non_disclosure"], + "disposition": "held", + "decision": "Owner/status/effective filtering is evidence; reject first-row-enabling ordering, raw grant projection, absent identity-link checks, and unsupported freshness claims." + }, + { + "path": "app/Services/Patient/PatientResponseDecorator.php", + "sha256": "d77359f42ad81b68eb4b10906009eadd24b7b6f26c31c70b2b0fb8ebad3d80e4", + "surface": "legacy_backend", + "categories": [ + "identity_input", + "enrollment_recovery", + "first_read_projection", + "error_non_disclosure" + ], + "disposition": "reimplement_principle_only", + "decision": "Central allowlisted code-to-copy mapping, framework-message suppression, no-store, no-index, and authorization variance are candidate controls; raw validation detail preservation, legacy codes, English-only copy, and decorator scope require independent review." + }, + { + "path": "app/Services/Patient/PatientResponseMetadata.php", + "sha256": "2f7724de26326d3aa20d8d6168a6e2e6bcadf8ca8eacde5916e9742056ecd73a", + "surface": "legacy_backend", + "categories": ["first_read_projection", "error_non_disclosure"], + "disposition": "held", + "decision": "Request ID, generated time, explicit freshness, policy, and vocabulary versions are candidate fields; aliases, default claims, and Hummingbird configuration sources are rejected." + }, + { + "path": "app/Services/Patient/Projection/PatientProjectionContentGuard.php", + "sha256": "55c1ef8f34750f4831eb98dd519824ba61344e2fe56a94c6c855c2db90772d9a", + "surface": "legacy_backend", + "categories": ["first_read_projection", "error_non_disclosure"], + "disposition": "reimplement_principle_only", + "decision": "Allowlist-first patient schema, forbidden source fields, raw-reference rejection, provenance, uncertainty, and canonical digest are strong candidate principles; exact content kinds, fields, enums, relationships, regexes, and copy remain held." + }, + { + "path": "app/Services/Patient/Projection/PatientProjectionDisclosureService.php", + "sha256": "9d5b5dfea90985f022b190baee18eb2826cf395b67c936576e50ef25fa418049", + "surface": "legacy_backend", + "categories": ["first_read_projection", "error_non_disclosure"], + "disposition": "reimplement_principle_only", + "decision": "Per-request ownership/scope/release recheck, anti-oracle collapse, latest released selection, audit-before-payload, and bounded provenance are evidence; legacy scopes, policy lookup, kinds, locks, data model, and copy cannot be reused." + }, + { + "path": "app/Services/Patient/Projection/PatientProjectionStateVocabulary.php", + "sha256": "6c0d8fc2b5e268612ff043c192c9d0ded06cb211af9a2e559c7a25f2db953f01", + "surface": "legacy_backend", + "categories": ["first_read_projection", "error_non_disclosure"], + "disposition": "held", + "decision": "Fail-closed code validation is a candidate principle; the split draft registry, labels, domains, version, locale ownership, and free-text boundary are not Nightingale-approved." + }, + { + "path": "tests/Feature/Patient/PatientApiBoundaryTest.php", + "sha256": "2235aca6ad0c2b59c43eb5db7b2eff3d74351b47ae98f4d0528a0cd13b1f2508", + "surface": "legacy_test", + "categories": [ + "identity_input", + "first_read_projection", + "error_non_disclosure" + ], + "disposition": "evidence_only", + "decision": "Reuse negative-test techniques for default-off, realm isolation, response shaping, and forbidden source fields; legacy paths, enabled fixtures, profile fields, and one-row success are not requirements." + }, + { + "path": "tests/Feature/Patient/PatientAuthLifecycleTest.php", + "sha256": "3603200dbd738e14a26679e761950f8f310c28cd32b6ebc30f5b96f7d6860d1d", + "surface": "legacy_test", + "categories": [ + "identity_input", + "enrollment_recovery", + "error_non_disclosure" + ], + "disposition": "evidence_only", + "decision": "Retain atomic enrollment, attempt lockout, generic credential denial, refresh rotation/reuse, revocation, cleanup, and audit cases as a test-idea catalog; no credential or route contract is approved." + }, + { + "path": "tests/Feature/Patient/PatientIdentityFoundationTest.php", + "sha256": "c6e1f158b82e50f52d999c38d177bb6dc422efe162b3b6971615142ed400ff4b", + "surface": "legacy_test", + "categories": [ + "identity_input", + "enrollment_recovery", + "first_read_projection", + "error_non_disclosure" + ], + "disposition": "evidence_only", + "decision": "Use encryption, hashing, external-handle, relationship, and append-only test patterns only; schema existence and account_recovery enum coverage do not constitute a recovery implementation." + }, + { + "path": "tests/Feature/Patient/PatientProjectionApiTest.php", + "sha256": "3775071196794e2856f05e35eeac86f7dd6ea1b074dd08549318416d171a9e2e", + "surface": "legacy_test", + "categories": ["first_read_projection", "error_non_disclosure"], + "disposition": "evidence_only", + "decision": "Retain unknown/cross-principal/revoked/expired/wrong-relationship/unreleased/retracted anti-oracle tests and no-store assertions as patterns; fixtures and projections remain Hummingbird-only." + }, + { + "path": "tests/Feature/Patient/PatientProjectionKernelTest.php", + "sha256": "80943a02526b6bfb5a5db79eea226a4cf138d5d95ab723cab2dc1be2de61dc83", + "surface": "legacy_test", + "categories": ["first_read_projection", "error_non_disclosure"], + "disposition": "evidence_only", + "decision": "Retain database and content-guard negative-test ideas; no legacy schema, content field, release state, or source fixture is approved." + }, + { + "path": "tests/Feature/Patient/PatientSessionManagementTest.php", + "sha256": "8ec0665944bc49c451af6ac40a50ab96e9ccefbbdea51f61a48a960286ad0bce", + "surface": "legacy_test", + "categories": [ + "identity_input", + "enrollment_recovery", + "error_non_disclosure" + ], + "disposition": "evidence_only", + "decision": "Owned-session enumeration, cross-principal equivalence, repeat revocation, current-session presence, and bounded collection are useful cases; device/session contract is held." + }, + { + "path": "tests/Feature/Patient/PatientTimestampContractTest.php", + "sha256": "77ab5ede20b2daa76c13cc49ea758a1b0d2318ca0176775fd3c42b602af3e470", + "surface": "legacy_test", + "categories": ["first_read_projection", "error_non_disclosure"], + "disposition": "evidence_only", + "decision": "UTC RFC 3339 and daylight-saving boundary cases are reusable test principles after Nightingale contract field ownership is approved." + }, + { + "path": "tests/Unit/Patient/PatientResponseMetadataTest.php", + "sha256": "4e7747e395aec252fb2ef50e043b866c483af43bdfcfbfe4de64cb4db965157a", + "surface": "legacy_test", + "categories": ["first_read_projection", "error_non_disclosure"], + "disposition": "evidence_only", + "decision": "Metadata merge and default tests reveal compatibility behavior; reissue against an exact Nightingale envelope and reject implicit legacy aliases." + }, + { + "path": "hummingbird/iosPatientApp/HummingbirdPatient/Features/Authentication/PatientWelcomeView.swift", + "sha256": "d8ac98e05769aa4835d2a59a86814f234517f1fb0dfa569c078a3b34ec23b25d", + "surface": "legacy_ios", + "categories": [ + "identity_input", + "enrollment_recovery", + "error_non_disclosure" + ], + "disposition": "reject", + "decision": "Reject email/password and seven-field invitation UI, Hummingbird copy, client-side assurance decisions, and state that is not explicitly cleared on background; retain accessible secure-field techniques as evidence only." + }, + { + "path": "hummingbird/iosPatientApp/HummingbirdPatient/App/PatientAppViewModel.swift", + "sha256": "74918da1c9af1a263b4535dec933c4c6acf4384bb7afa195997a26e5e1c37694", + "surface": "legacy_ios", + "categories": [ + "identity_input", + "enrollment_recovery", + "first_read_projection", + "error_non_disclosure" + ], + "disposition": "reject", + "decision": "Reject credential orchestration, silent first-encounter selection, raw-scope fan-out, not-found and incompatible-vocabulary collapse, all-or-nothing projection tuple, and server-message passthrough; retain token zeroization and storage-failure revocation ideas only." + }, + { + "path": "hummingbird/iosPatientApp/HummingbirdPatient/Networking/PatientAPIClient.swift", + "sha256": "72a8b54098bfd19402812aa0e2ef367154aa589d8f1bd7eb7f03b99b89ba9e8b", + "surface": "legacy_ios", + "categories": [ + "identity_input", + "enrollment_recovery", + "first_read_projection", + "error_non_disclosure" + ], + "disposition": "reject", + "decision": "Reject the legacy base path, production host, operations, bearer contract, redirect/error mapping, and decoded server messages; future Nightingale transport must be generated from its own approved contract and fail closed." + }, + { + "path": "hummingbird/iosPatientApp/HummingbirdPatient/Networking/PatientAPIModels.swift", + "sha256": "93c3250b7292c9abe17e3bb932e39c489332c8becc321663b4b8365a1e468759", + "surface": "legacy_ios", + "categories": [ + "identity_input", + "enrollment_recovery", + "first_read_projection", + "error_non_disclosure" + ], + "disposition": "reject", + "decision": "Reject legacy token, profile, grant, projection, error, enrollment, device, auth-method, and recovery enum models; use decoding strictness gaps and field inventory as evidence." + }, + { + "path": "hummingbird/iosPatientApp/HummingbirdPatient/Networking/PatientSecureStore.swift", + "sha256": "4383411a5e554228b17aae9be0cf82f980b245116b3998a1180eead43f9b1016", + "surface": "legacy_ios", + "categories": [ + "identity_input", + "enrollment_recovery", + "error_non_disclosure" + ], + "disposition": "reject", + "decision": "Reject Hummingbird Keychain service, access/refresh token schema, and credential migration; only device-only, non-synchronizing storage behavior was independently reimplemented in the Nightingale foundation." + }, + { + "path": "hummingbird/iosPatientApp/HummingbirdPatient/Models/PatientExperienceSnapshot.swift", + "sha256": "15342e15a634243bbbedffda4f0b07a0a9a9d75fbdfdc0c38c1c8e121faee450", + "surface": "legacy_ios", + "categories": ["first_read_projection", "error_non_disclosure"], + "disposition": "reject", + "decision": "Reject first-record selection, random UUID substitution for malformed server identifiers, generic empty-state inference, raw-scope retention, source-copy fallback, and synthetic production-shaped snapshot." + }, + { + "path": "hummingbird/iosPatientApp/HummingbirdPatient/Models/PatientStateVocabulary.swift", + "sha256": "e13015b0f09f58f845d64d202839d16be1ea36744b35445d95e6ef6904da231a", + "surface": "legacy_ios", + "categories": ["first_read_projection", "error_non_disclosure"], + "disposition": "held", + "decision": "Version compatibility and patient labels require an independently checksum-bound registry, localization, missing/unknown state contract, and approvals; no label or code migrates." + }, + { + "path": "hummingbird/iosPatientApp/HummingbirdPatientTests/PatientAPIBoundaryTests.swift", + "sha256": "d50f0ad0f003447658df322d061d56157bf08e125d27ab2a4c51a1880c6aef41", + "surface": "legacy_ios_test", + "categories": [ + "identity_input", + "enrollment_recovery", + "first_read_projection", + "error_non_disclosure" + ], + "disposition": "evidence_only", + "decision": "Reuse route allowlist, HTTPS, host, identifier, redirect, storage-service, and product-boundary negative-test techniques against Nightingale-owned values only." + }, + { + "path": "hummingbird/iosPatientApp/HummingbirdPatientTests/PatientAPIClientTests.swift", + "sha256": "b96bfbdfca371903c5aba06a609a2c511798a08545ec5889b8d1c9ebf110c608", + "surface": "legacy_ios_test", + "categories": [ + "identity_input", + "enrollment_recovery", + "first_read_projection", + "error_non_disclosure" + ], + "disposition": "evidence_only", + "decision": "Retain transport, header, token, redirect, envelope, and error negative-test patterns; every asserted Hummingbird route and message is non-authoritative." + }, + { + "path": "hummingbird/iosPatientApp/HummingbirdPatientTests/PatientAPIModelTests.swift", + "sha256": "2e461608ad1db8af66e204ba0396e864826adc71ebf4533d494390ebd138b95a", + "surface": "legacy_ios_test", + "categories": ["first_read_projection", "error_non_disclosure"], + "disposition": "evidence_only", + "decision": "Reuse malformed-envelope, timestamp, field, vocabulary, and projection-decoding test techniques only after an approved Nightingale schema exists." + }, + { + "path": "hummingbird/iosPatientApp/HummingbirdPatientTests/PatientAppViewModelTests.swift", + "sha256": "4938eefb40b613e3e43476d4fadc1bdbde04604155b8b268db25818ec2f58b26", + "surface": "legacy_ios_test", + "categories": [ + "identity_input", + "enrollment_recovery", + "first_read_projection", + "error_non_disclosure" + ], + "disposition": "evidence_only", + "decision": "Test doubles expose first-row, scope, projection, credential, session, and message behavior; use them as a gap catalog, not as Nightingale acceptance criteria." + }, + { + "path": "hummingbird/androidPatientApp/app/src/main/java/net/acumenus/hummingbird/patient/PatientAppViewModel.kt", + "sha256": "dfa4d35f325b8199a66af67978e074aafd4364487944f5609a6ec93f5305d63e", + "surface": "legacy_android", + "categories": [ + "identity_input", + "enrollment_recovery", + "first_read_projection", + "error_non_disclosure" + ], + "disposition": "reject", + "decision": "Reject legacy identity UI orchestration, product copy, client assurance, scope-derived availability, and status-message policy; retain char-array clearing and generic messaging-failure patterns only as evidence." + }, + { + "path": "hummingbird/androidPatientApp/app/src/main/java/net/acumenus/hummingbird/patient/PatientExperienceModels.kt", + "sha256": "4b9eb48815c34248d8f6580e4c72dd9c8a014ce6bce3f3810d82a2adadc8fcf8", + "surface": "legacy_android", + "categories": [ + "identity_input", + "enrollment_recovery", + "first_read_projection", + "error_non_disclosure" + ], + "disposition": "reject", + "decision": "Reject legacy auth modes, enrollment form, raw encounter scopes, destination state, projection models, and patient copy; define Nightingale models from an approved contract." + }, + { + "path": "hummingbird/androidPatientApp/app/src/main/java/net/acumenus/hummingbird/patient/data/PatientApiClient.kt", + "sha256": "9f803c7533f5bd7b2d2877777e8274b17a7fa0c8455395b4b3ddfe4ded26e3f5", + "surface": "legacy_android", + "categories": [ + "identity_input", + "enrollment_recovery", + "first_read_projection", + "error_non_disclosure" + ], + "disposition": "reject", + "decision": "Reject the 23-path allowlist, Hummingbird host/base path, bearer/token/device/enrollment contract, and exception messages decoded from the server; retain no-cache/no-redirect and URL validation techniques only." + }, + { + "path": "hummingbird/androidPatientApp/app/src/main/java/net/acumenus/hummingbird/patient/data/PatientApiModels.kt", + "sha256": "a1932c3dc2f037f4006c72e083d57bc6219f63de42f348c7909737707d25e115", + "surface": "legacy_android", + "categories": [ + "identity_input", + "enrollment_recovery", + "first_read_projection", + "error_non_disclosure" + ], + "disposition": "reject", + "decision": "Reject legacy token, profile, grant, projection, error, device, session, enrollment, and recovery models; decoder validations are evidence for a new fail-closed schema." + }, + { + "path": "hummingbird/androidPatientApp/app/src/main/java/net/acumenus/hummingbird/patient/data/PatientSecureStore.kt", + "sha256": "f891b4a587c99832d14d0e8db27754c689507bce2c14dbf006af7b8d42047bbe", + "surface": "legacy_android", + "categories": [ + "identity_input", + "enrollment_recovery", + "error_non_disclosure" + ], + "disposition": "reject", + "decision": "Reject EncryptedSharedPreferences, legacy access/refresh/session values, durable device UUID, alias, and migration; the Nightingale foundation already uses an independent token-agnostic Keystore primitive." + }, + { + "path": "hummingbird/androidPatientApp/app/src/main/java/net/acumenus/hummingbird/patient/data/PatientSessionCoordinator.kt", + "sha256": "10bfbe8782746390ea71242d1c95ff5f09809b61be40533f91cba1e9acfaada2", + "surface": "legacy_android", + "categories": [ + "identity_input", + "enrollment_recovery", + "first_read_projection", + "error_non_disclosure" + ], + "disposition": "reject", + "decision": "Reject email/password and challenge exchange, refresh persistence, firstOrNull encounter selection, client-scope fan-out, 403/404 projection collapse, incompatible-vocabulary nulling, and inferred empty/fallback states; retain secret clearing and storage-failure revoke ideas only." + }, + { + "path": "hummingbird/androidPatientApp/app/src/main/java/net/acumenus/hummingbird/patient/data/PatientStateVocabulary.kt", + "sha256": "023157f23c8aa372f6ef10e1d07086603cb507d446e9a18798cb3fcbf953660e", + "surface": "legacy_android", + "categories": ["first_read_projection", "error_non_disclosure"], + "disposition": "held", + "decision": "No state code or label migrates; Nightingale requires one checksum-bound cross-platform registry, localization, exact incompatibility handling, and named review." + }, + { + "path": "hummingbird/androidPatientApp/app/src/main/java/net/acumenus/hummingbird/patient/ui/PatientAuthenticationScreen.kt", + "sha256": "625eed90c62f8623844d915762289421b4580abb0fa6452f32e10d9e66d27e1a", + "surface": "legacy_android", + "categories": [ + "identity_input", + "enrollment_recovery", + "error_non_disclosure" + ], + "disposition": "reject", + "decision": "Reject email/password and seven-field invitation UX, Hummingbird copy, and unapproved support path; volatile Compose state and post-submit secret clearing are useful evidence but do not cover lifecycle/background clearing." + }, + { + "path": "hummingbird/androidPatientApp/app/src/test/java/net/acumenus/hummingbird/patient/PatientAppViewModelTest.kt", + "sha256": "58bfa794fda3f8b40c72d9f619ebe593668fc1379cafdbb7cb894a4ca468d0e6", + "surface": "legacy_android_test", + "categories": [ + "identity_input", + "enrollment_recovery", + "first_read_projection", + "error_non_disclosure" + ], + "disposition": "evidence_only", + "decision": "Retain state-transition, secret-clearing, generic-error, and API-disabled test ideas; asserted Hummingbird flows and copy are not Nightingale requirements." + }, + { + "path": "hummingbird/androidPatientApp/app/src/test/java/net/acumenus/hummingbird/patient/data/PatientEndpointBoundaryTest.kt", + "sha256": "92edaed877c4cd3f2eb02eb3829bc8083a14cc84492592b9bbdc492d54c87c50", + "surface": "legacy_android_test", + "categories": [ + "identity_input", + "enrollment_recovery", + "first_read_projection", + "error_non_disclosure" + ], + "disposition": "evidence_only", + "decision": "Reuse exact operation allowlist, HTTPS, host, identifier, and disabled-network negative-test techniques against Nightingale-owned operations only." + }, + { + "path": "hummingbird/androidPatientApp/app/src/test/java/net/acumenus/hummingbird/patient/data/PatientEnvelopeDecoderTest.kt", + "sha256": "ca83242060bce3c21d62e30ffa3a1bb9566373adb51c5b88788f2b62eb56e81d", + "surface": "legacy_android_test", + "categories": [ + "identity_input", + "first_read_projection", + "error_non_disclosure" + ], + "disposition": "evidence_only", + "decision": "Reuse malformed-envelope, UUID, length, timestamp, vocabulary, revision, and bounded-list cases only after a Nightingale schema is approved." + }, + { + "path": "hummingbird/androidPatientApp/app/src/test/java/net/acumenus/hummingbird/patient/data/PatientSessionCoordinatorTest.kt", + "sha256": "7918d69d4e751e0f063c1061db1b3a1289a5d145fc5edb1a9571399d5408e9db", + "surface": "legacy_android_test", + "categories": [ + "identity_input", + "enrollment_recovery", + "first_read_projection", + "error_non_disclosure" + ], + "disposition": "evidence_only", + "decision": "Use refresh, revocation, storage-failure, first-read, projection-absence, and vocabulary cases as a gap catalog; no legacy outcome is automatically correct for Nightingale." + } + ] +} diff --git a/docs/nightingale/namespace/foundation-namespace.v1.json b/docs/nightingale/namespace/foundation-namespace.v1.json new file mode 100644 index 00000000..c57ce7ec --- /dev/null +++ b/docs/nightingale/namespace/foundation-namespace.v1.json @@ -0,0 +1,164 @@ +{ + "schema_version": 1, + "contract_id": "nightingale-foundation-namespace.v1", + "status": "offline_foundation_only", + "generated_at": "2026-07-27", + "product": { + "name": "Nightingale", + "reverse_dns_prefix": "net.acumenus.nightingale", + "ios_bundle_id": "net.acumenus.nightingale", + "android_application_id": "net.acumenus.nightingale" + }, + "namespace_rules": { + "accessibility_identifier_prefix": "nightingale-", + "test_hook_prefix": "NIGHTINGALE_", + "storage_identifier_prefix": "net.acumenus.nightingale.", + "telemetry_event_prefix": "net.acumenus.nightingale.telemetry.v1.", + "diagnostic_channel_prefix": "net.acumenus.nightingale.diagnostics.v1." + }, + "inventories": { + "accessibility_identifiers": [ + "nightingale-safe-shell", + "nightingale-product-heading", + "nightingale-foundation-mission", + "nightingale-privacy-status-heading", + "nightingale-display-comfort-heading", + "nightingale-reduce-motion-toggle", + "nightingale-motion-status", + "nightingale-hide-imagery-toggle", + "nightingale-imagery-status", + "nightingale-privacy-cover" + ], + "test_hooks": [ + "NIGHTINGALE_SHOW_PRIVACY_COVER", + "NIGHTINGALE_TEST_ACCESSIBILITY_TEXT_SIZE", + "NIGHTINGALE_TEST_LAYOUT_DIRECTION", + "NIGHTINGALE_TEST_RESET_PRESENTATION_PREFERENCES" + ], + "storage_identifiers": [ + { + "platform": "ios", + "kind": "keychain_service", + "value": "net.acumenus.nightingale.protected-state.v1" + }, + { + "platform": "ios", + "kind": "keychain_account", + "value": "net.acumenus.nightingale.protected-state.v1.future-session-binding" + }, + { + "platform": "ios", + "kind": "preference_key", + "value": "net.acumenus.nightingale.presentation.v1.reduce-motion" + }, + { + "platform": "ios", + "kind": "preference_key", + "value": "net.acumenus.nightingale.presentation.v1.hide-decorative-imagery" + }, + { + "platform": "android", + "kind": "preference_file", + "value": "net.acumenus.nightingale.presentation.v1" + }, + { + "platform": "android", + "kind": "preference_key", + "value": "net.acumenus.nightingale.presentation.v1.reduce-motion" + }, + { + "platform": "android", + "kind": "preference_key", + "value": "net.acumenus.nightingale.presentation.v1.hide-decorative-imagery" + }, + { + "platform": "android", + "kind": "keystore_alias", + "value": "net.acumenus.nightingale.protected-state-key.v1" + }, + { + "platform": "android", + "kind": "preference_file", + "value": "net.acumenus.nightingale.protected-state-ciphertext.v1" + }, + { + "platform": "android", + "kind": "preference_key", + "value": "net.acumenus.nightingale.protected-state.v1.future-session-binding" + } + ], + "telemetry_event_names": [], + "diagnostic_channel_names": [] + }, + "constraints": { + "legacy_hummingbird_namespace_permitted": false, + "legacy_patient_product_copy_permitted": false, + "unnamespaced_storage_identifier_permitted": false, + "release_test_hooks_permitted": false, + "telemetry_implemented": false, + "diagnostics_implemented": false, + "runtime_logging_permitted": false + }, + "sources": [ + { + "path": "nightingale/iosApp/project.yml", + "sha256": "9eaa4650ee985b80661d32d206ec71d2d2a1b44d87e430d48343d22f35f7cb85", + "responsibility": "iOS application, unit-test, and UI-test bundle identities" + }, + { + "path": "nightingale/iosApp/Nightingale/Info.plist", + "sha256": "87200056bdba510176b77bcc3254b71f2b079a5736891f3d4dadcee9f1056881", + "responsibility": "iOS patient-visible product identity" + }, + { + "path": "nightingale/iosApp/Nightingale/Localizable.xcstrings", + "sha256": "352bdb164b710ffbb8257dd9529a94337dc08e834e4dc24eff08845a8cc442ea", + "responsibility": "iOS product-owned patient copy catalog" + }, + { + "path": "nightingale/iosApp/Nightingale/NightingaleApp.swift", + "sha256": "8a150ba7f752e096684cf417befa51d4e09cbf252db404d7d1564f9acf2bc6ed", + "responsibility": "iOS Debug hooks and accessibility identifiers" + }, + { + "path": "nightingale/iosApp/Nightingale/NightingaleVisualFoundation.swift", + "sha256": "0018b4743eb01a57d431293460e640c1c6d3eac9e8fdc2d92996ddfc82cf3288", + "responsibility": "iOS privacy-cover accessibility identifier" + }, + { + "path": "nightingale/iosApp/Nightingale/NightingalePresentationPreferences.swift", + "sha256": "794a6e39431a458cc271447083a3d8326897d8964a91c4823feff85e3a9aa1dd", + "responsibility": "iOS presentation storage keys and Debug reset hook" + }, + { + "path": "nightingale/iosApp/Nightingale/NightingaleProtectedState.swift", + "sha256": "5a6df00950917842d40d8b198f71c92070a2adc2471cd95cb74dcd7d3fe7ca1c", + "responsibility": "iOS protected-state service and account keys" + }, + { + "path": "nightingale/androidApp/app/build.gradle.kts", + "sha256": "d3a333b3f397b71fbd37647fcf8878f85e75a4e2fa09f9b109448fac86707ab6", + "responsibility": "Android application and test package identities" + }, + { + "path": "nightingale/androidApp/app/src/main/res/values/strings.xml", + "sha256": "73e153be31040b56583075acfce8c79a0947d55f5ff8b35275677568375f28f1", + "responsibility": "Android product-owned patient copy catalog" + }, + { + "path": "nightingale/androidApp/app/src/main/java/net/acumenus/nightingale/NightingaleVisualFoundation.kt", + "sha256": "b2f37c6f76dbad695bb2834c0c8c9adf4f0028ef5ad2b94138719082adcf2b46", + "responsibility": "Android patient-shell semantics and test-tag identifiers" + }, + { + "path": "nightingale/androidApp/app/src/main/java/net/acumenus/nightingale/NightingalePresentationPreferences.kt", + "sha256": "338bbb7177df79b73a8a3168ce2803ee3474e78a4f42900f1e208ac8938993a1", + "responsibility": "Android presentation storage file and keys" + }, + { + "path": "nightingale/androidApp/app/src/main/java/net/acumenus/nightingale/NightingaleProtectedState.kt", + "sha256": "ca3528dfd55bb4d9681f06916b9f44b7cb1fe1cde51771d42eb6c2a61811dbed", + "responsibility": "Android protected-state aliases, file, key, and authenticated context" + } + ] +} diff --git a/docs/nightingale/pilot/candidates/v0/candidate.json b/docs/nightingale/pilot/candidates/v0/candidate.json new file mode 100644 index 00000000..41da12c1 --- /dev/null +++ b/docs/nightingale/pilot/candidates/v0/candidate.json @@ -0,0 +1,223 @@ +{ + "candidate_id": "nightingale.controlled-pilot-manifest.v0-candidate", + "policy_version": "nightingale-controlled-pilot-manifest.v0-candidate", + "schema_version": 0, + "status": "draft_default_off_not_approved_for_pilot", + "generated_at": "2026-07-27T20:00:00Z", + "reviewed_source_commit": "b333ebf8a618cac5c3c8a27d43ea666de32579bc", + "product": "Nightingale", + "purpose": "Define the exact facility, unit, cohort, language, exclusion, support-hour, validity, audit, rollback, and approval shape required before a future controlled-pilot go/no-go review.", + "disposition_vocabulary": [ + "hold", + "eligible_for_external_go_no_go_review_only" + ], + "eligibility_boundary": { + "positive_disposition_is_activation": false, + "positive_disposition_is_enrollment_authorization": false, + "positive_disposition_is_deployment_authorization": false, + "positive_disposition_is_patient_disclosure_authorization": false, + "external_go_no_go_record_still_required": true, + "runtime_evaluator_implemented": false, + "runtime_callers": 0 + }, + "technical_ceilings": { + "maximum_validity_hours": 168, + "maximum_active_enrollments": 25, + "automatic_enrollment_permitted": false + }, + "required_approval_roles": [ + "product_owner", + "clinical_safety_owner", + "privacy_security_owner", + "patient_advisor_accessibility_owner", + "identity_source_owner", + "operations_support_owner", + "release_owner" + ], + "required_audit_event_types": [ + "manifest_created", + "manifest_review_requested", + "manifest_approved", + "go_no_go_requested", + "pilot_started", + "pilot_expired", + "pilot_withdrawn", + "kill_switch_invoked", + "rollback_completed" + ], + "required_audit_fields": [ + "manifest_id", + "manifest_revision", + "event_type", + "occurred_at", + "actor_role", + "actor_reference_hash", + "outcome", + "reason_code", + "policy_version" + ], + "committed_default_manifest": { + "manifest_id": "nightingale.controlled-pilot.unissued", + "manifest_revision": 0, + "policy_version": "nightingale-controlled-pilot-manifest.v0-candidate", + "state": "draft_inactive", + "go_no_go_review_requested": false, + "runtime_activation_permitted": false, + "scope": { + "facility_handles": [], + "unit_handles": [], + "cohort": { + "cohort_handle": null, + "inclusion_policy_release_id": null, + "exclusion_policy_release_id": null, + "maximum_active_enrollments": 0, + "enrollment_is_automatic": false, + "unknown_or_unavailable_eligibility_result": "withhold" + }, + "languages": { + "patient_locale_tags": [], + "interpreter_coverage_release_id": null, + "unapproved_locale_fallback_permitted": false, + "unknown_language_disposition": "withhold" + }, + "exclusions": { + "exclusion_policy_release_id": null, + "deny_if_rule_result_unknown": true, + "deny_if_rule_source_unavailable": true, + "sensitive_service_inference_permitted": false, + "exclusion_reason_disclosure_permitted": false + } + }, + "support_coverage": { + "timezone": null, + "weekly_windows": [], + "urgent_help_content_release_id": null, + "uncovered_window_disposition": "withhold_new_enrollment_and_show_released_support_guidance", + "support_availability_may_be_inferred": false + }, + "validity": { + "starts_at": null, + "expires_at": null, + "maximum_validity_hours": 168, + "renewal_requires_new_manifest": true, + "expiry_fails_closed": true + }, + "prerequisites": { + "clinical_approval_record": null, + "patient_content_release_record": null, + "feature_activation_record": null, + "source_connector_deployment_record": null, + "identity_source_approval_record": null, + "audit_sink_deployment_record": null, + "rollback_plan_record": null, + "kill_switch_test_record": null + }, + "approvals": { + "product_owner": { + "approver_subject": null, + "approval_record": null, + "approved_at": null + }, + "clinical_safety_owner": { + "approver_subject": null, + "approval_record": null, + "approved_at": null + }, + "privacy_security_owner": { + "approver_subject": null, + "approval_record": null, + "approved_at": null + }, + "patient_advisor_accessibility_owner": { + "approver_subject": null, + "approval_record": null, + "approved_at": null + }, + "identity_source_owner": { + "approver_subject": null, + "approval_record": null, + "approved_at": null + }, + "operations_support_owner": { + "approver_subject": null, + "approval_record": null, + "approved_at": null + }, + "release_owner": { + "approver_subject": null, + "approval_record": null, + "approved_at": null + } + }, + "audit": { + "append_only_required": true, + "durable_before_effective_change": true, + "event_schema_release_id": null, + "required_event_types": [ + "manifest_created", + "manifest_review_requested", + "manifest_approved", + "go_no_go_requested", + "pilot_started", + "pilot_expired", + "pilot_withdrawn", + "kill_switch_invoked", + "rollback_completed" + ], + "required_fields": [ + "manifest_id", + "manifest_revision", + "event_type", + "occurred_at", + "actor_role", + "actor_reference_hash", + "outcome", + "reason_code", + "policy_version" + ], + "patient_identifiers_permitted": false, + "clinical_content_permitted": false, + "message_content_permitted": false, + "audit_failure_disposition": "withhold" + }, + "rollback": { + "kill_switch_default_state": "engaged", + "rollback_target_release_id": null, + "rollback_verification_record": null, + "enrollment_and_disclosure_after_expiry_permitted": false + } + }, + "constraints": { + "runtime_implementation_permitted": false, + "route_registration_permitted": false, + "native_networking_permitted": false, + "identity_provider_selection_permitted": false, + "source_adapter_selection_permitted": false, + "production_query_permitted": false, + "patient_or_representative_creation_permitted": false, + "patient_enrollment_permitted": false, + "patient_disclosure_permitted": false, + "patient_mutation_permitted": false, + "communication_or_notification_permitted": false, + "deployment_permitted": false, + "pilot_activation_permitted": false + }, + "source_evidence": [ + { + "path": "config/nightingale.php", + "sha256": "547fa76746ba3f7926bbdf380bb8b7d7308d0413e747e9b01d228a6483c41579" + }, + { + "path": "docs/nightingale/api-contract/nightingale-foundation.v0.json", + "sha256": "6765da41f884cec3cb6596e2484a47297878940731b306a1b5ecf664cf7c0580" + }, + { + "path": "app/Nightingale/Activation/NightingaleActivationGate.php", + "sha256": "d9c20d2844b2e7a0d23ac22510afb3857682f79609721466382804b45ac5b5eb" + }, + { + "path": "app/Nightingale/Activation/NightingalePilotEnrollmentState.php", + "sha256": "9e06cb30f344f269521731fd7b28d646ab9b77ab53cf0fb301800f999c8ae637" + } + ] +} diff --git a/docs/nightingale/pilot/candidates/v0/fixtures.json b/docs/nightingale/pilot/candidates/v0/fixtures.json new file mode 100644 index 00000000..dabe6330 --- /dev/null +++ b/docs/nightingale/pilot/candidates/v0/fixtures.json @@ -0,0 +1,604 @@ +{ + "fixture_set_id": "nightingale.controlled-pilot-manifest.v0-fixtures", + "fixture_class": "synthetic-no-phi", + "evaluation_time": "2026-07-27T20:00:00Z", + "complete_synthetic_manifest": { + "manifest_id": "nightingale.controlled-pilot.synthetic-review-only", + "manifest_revision": 1, + "policy_version": "nightingale-controlled-pilot-manifest.v0-candidate", + "state": "approved_inactive", + "go_no_go_review_requested": true, + "runtime_activation_permitted": false, + "scope": { + "facility_handles": ["ngf_synthetic_alpha"], + "unit_handles": ["ngu_synthetic_medical_01"], + "cohort": { + "cohort_handle": "ngc_synthetic_adult_inpatient_01", + "inclusion_policy_release_id": "synthetic-inclusion-release-v1", + "exclusion_policy_release_id": "synthetic-exclusion-release-v1", + "maximum_active_enrollments": 10, + "enrollment_is_automatic": false, + "unknown_or_unavailable_eligibility_result": "withhold" + }, + "languages": { + "patient_locale_tags": ["en-US", "es-US"], + "interpreter_coverage_release_id": "synthetic-interpreter-coverage-release-v1", + "unapproved_locale_fallback_permitted": false, + "unknown_language_disposition": "withhold" + }, + "exclusions": { + "exclusion_policy_release_id": "synthetic-exclusion-release-v1", + "deny_if_rule_result_unknown": true, + "deny_if_rule_source_unavailable": true, + "sensitive_service_inference_permitted": false, + "exclusion_reason_disclosure_permitted": false + } + }, + "support_coverage": { + "timezone": "America/New_York", + "weekly_windows": [ + { + "iso_weekday": 1, + "starts_at_local": "08:00", + "ends_at_local": "18:00" + }, + { + "iso_weekday": 2, + "starts_at_local": "08:00", + "ends_at_local": "18:00" + }, + { + "iso_weekday": 3, + "starts_at_local": "08:00", + "ends_at_local": "18:00" + }, + { + "iso_weekday": 4, + "starts_at_local": "08:00", + "ends_at_local": "18:00" + }, + { + "iso_weekday": 5, + "starts_at_local": "08:00", + "ends_at_local": "18:00" + } + ], + "urgent_help_content_release_id": "synthetic-urgent-help-content-release-v1", + "uncovered_window_disposition": "withhold_new_enrollment_and_show_released_support_guidance", + "support_availability_may_be_inferred": false + }, + "validity": { + "starts_at": "2026-07-27T19:00:00Z", + "expires_at": "2026-08-03T19:00:00Z", + "maximum_validity_hours": 168, + "renewal_requires_new_manifest": true, + "expiry_fails_closed": true + }, + "prerequisites": { + "clinical_approval_record": "synthetic-clinical-approval-v1", + "patient_content_release_record": "synthetic-content-release-v1", + "feature_activation_record": "synthetic-feature-activation-v1", + "source_connector_deployment_record": "synthetic-source-deployment-v1", + "identity_source_approval_record": "synthetic-identity-source-approval-v1", + "audit_sink_deployment_record": "synthetic-audit-sink-v1", + "rollback_plan_record": "synthetic-rollback-plan-v1", + "kill_switch_test_record": "synthetic-kill-switch-test-v1" + }, + "approvals": { + "product_owner": { + "approver_subject": "synthetic-product-owner", + "approval_record": "synthetic-product-owner-approval-v1", + "approved_at": "2026-07-27T18:00:00Z" + }, + "clinical_safety_owner": { + "approver_subject": "synthetic-clinical-safety-owner", + "approval_record": "synthetic-clinical-safety-owner-approval-v1", + "approved_at": "2026-07-27T18:00:00Z" + }, + "privacy_security_owner": { + "approver_subject": "synthetic-privacy-security-owner", + "approval_record": "synthetic-privacy-security-owner-approval-v1", + "approved_at": "2026-07-27T18:00:00Z" + }, + "patient_advisor_accessibility_owner": { + "approver_subject": "synthetic-patient-advisor-accessibility-owner", + "approval_record": "synthetic-patient-advisor-accessibility-owner-approval-v1", + "approved_at": "2026-07-27T18:00:00Z" + }, + "identity_source_owner": { + "approver_subject": "synthetic-identity-source-owner", + "approval_record": "synthetic-identity-source-owner-approval-v1", + "approved_at": "2026-07-27T18:00:00Z" + }, + "operations_support_owner": { + "approver_subject": "synthetic-operations-support-owner", + "approval_record": "synthetic-operations-support-owner-approval-v1", + "approved_at": "2026-07-27T18:00:00Z" + }, + "release_owner": { + "approver_subject": "synthetic-release-owner", + "approval_record": "synthetic-release-owner-approval-v1", + "approved_at": "2026-07-27T18:00:00Z" + } + }, + "audit": { + "append_only_required": true, + "durable_before_effective_change": true, + "event_schema_release_id": "synthetic-audit-event-schema-v1", + "required_event_types": [ + "manifest_created", + "manifest_review_requested", + "manifest_approved", + "go_no_go_requested", + "pilot_started", + "pilot_expired", + "pilot_withdrawn", + "kill_switch_invoked", + "rollback_completed" + ], + "required_fields": [ + "manifest_id", + "manifest_revision", + "event_type", + "occurred_at", + "actor_role", + "actor_reference_hash", + "outcome", + "reason_code", + "policy_version" + ], + "patient_identifiers_permitted": false, + "clinical_content_permitted": false, + "message_content_permitted": false, + "audit_failure_disposition": "withhold" + }, + "rollback": { + "kill_switch_default_state": "engaged", + "rollback_target_release_id": "synthetic-offline-foundation-release-v1", + "rollback_verification_record": "synthetic-rollback-verification-v1", + "enrollment_and_disclosure_after_expiry_permitted": false + } + }, + "cases": [ + { + "case_id": "complete_synthetic_candidate", + "input": "complete_synthetic_manifest", + "mutations": [], + "expected_disposition": "eligible_for_external_go_no_go_review_only", + "expected_primary_reason": null + }, + { + "case_id": "committed_default_template", + "input": "committed_default_template", + "mutations": [], + "expected_disposition": "hold", + "expected_primary_reason": "manifest_not_approved_inactive" + }, + { + "case_id": "draft_state", + "input": "complete_synthetic_manifest", + "mutations": [ + { + "path": "state", + "value": "draft_inactive" + } + ], + "expected_disposition": "hold", + "expected_primary_reason": "manifest_not_approved_inactive" + }, + { + "case_id": "go_no_go_not_requested", + "input": "complete_synthetic_manifest", + "mutations": [ + { + "path": "go_no_go_review_requested", + "value": false + } + ], + "expected_disposition": "hold", + "expected_primary_reason": "go_no_go_review_not_requested" + }, + { + "case_id": "runtime_activation_attempted", + "input": "complete_synthetic_manifest", + "mutations": [ + { + "path": "runtime_activation_permitted", + "value": true + } + ], + "expected_disposition": "hold", + "expected_primary_reason": "runtime_activation_must_remain_prohibited" + }, + { + "case_id": "facility_scope_empty", + "input": "complete_synthetic_manifest", + "mutations": [ + { + "path": "scope.facility_handles", + "value": [] + } + ], + "expected_disposition": "hold", + "expected_primary_reason": "facility_scope_missing_or_invalid" + }, + { + "case_id": "unit_scope_empty", + "input": "complete_synthetic_manifest", + "mutations": [ + { + "path": "scope.unit_handles", + "value": [] + } + ], + "expected_disposition": "hold", + "expected_primary_reason": "unit_scope_missing_or_invalid" + }, + { + "case_id": "cohort_handle_missing", + "input": "complete_synthetic_manifest", + "mutations": [ + { + "path": "scope.cohort.cohort_handle", + "value": null + } + ], + "expected_disposition": "hold", + "expected_primary_reason": "cohort_handle_missing_or_invalid" + }, + { + "case_id": "cohort_inclusion_release_missing", + "input": "complete_synthetic_manifest", + "mutations": [ + { + "path": "scope.cohort.inclusion_policy_release_id", + "value": null + } + ], + "expected_disposition": "hold", + "expected_primary_reason": "cohort_policy_release_missing" + }, + { + "case_id": "cohort_exclusion_release_mismatch", + "input": "complete_synthetic_manifest", + "mutations": [ + { + "path": "scope.cohort.exclusion_policy_release_id", + "value": "synthetic-other-exclusion-release-v1" + } + ], + "expected_disposition": "hold", + "expected_primary_reason": "exclusion_policy_release_mismatch" + }, + { + "case_id": "cohort_limit_zero", + "input": "complete_synthetic_manifest", + "mutations": [ + { + "path": "scope.cohort.maximum_active_enrollments", + "value": 0 + } + ], + "expected_disposition": "hold", + "expected_primary_reason": "cohort_enrollment_limit_invalid" + }, + { + "case_id": "cohort_limit_exceeds_ceiling", + "input": "complete_synthetic_manifest", + "mutations": [ + { + "path": "scope.cohort.maximum_active_enrollments", + "value": 26 + } + ], + "expected_disposition": "hold", + "expected_primary_reason": "cohort_enrollment_limit_invalid" + }, + { + "case_id": "automatic_enrollment_attempted", + "input": "complete_synthetic_manifest", + "mutations": [ + { + "path": "scope.cohort.enrollment_is_automatic", + "value": true + } + ], + "expected_disposition": "hold", + "expected_primary_reason": "automatic_enrollment_prohibited" + }, + { + "case_id": "language_scope_empty", + "input": "complete_synthetic_manifest", + "mutations": [ + { + "path": "scope.languages.patient_locale_tags", + "value": [] + } + ], + "expected_disposition": "hold", + "expected_primary_reason": "language_scope_missing_or_invalid" + }, + { + "case_id": "language_tag_invalid", + "input": "complete_synthetic_manifest", + "mutations": [ + { + "path": "scope.languages.patient_locale_tags", + "value": ["English"] + } + ], + "expected_disposition": "hold", + "expected_primary_reason": "language_scope_missing_or_invalid" + }, + { + "case_id": "interpreter_coverage_missing", + "input": "complete_synthetic_manifest", + "mutations": [ + { + "path": "scope.languages.interpreter_coverage_release_id", + "value": null + } + ], + "expected_disposition": "hold", + "expected_primary_reason": "interpreter_coverage_release_missing" + }, + { + "case_id": "unapproved_language_fallback_attempted", + "input": "complete_synthetic_manifest", + "mutations": [ + { + "path": "scope.languages.unapproved_locale_fallback_permitted", + "value": true + } + ], + "expected_disposition": "hold", + "expected_primary_reason": "unapproved_language_fallback_prohibited" + }, + { + "case_id": "unknown_exclusion_result_not_denied", + "input": "complete_synthetic_manifest", + "mutations": [ + { + "path": "scope.exclusions.deny_if_rule_result_unknown", + "value": false + } + ], + "expected_disposition": "hold", + "expected_primary_reason": "exclusion_unknown_must_deny" + }, + { + "case_id": "exclusion_source_outage_not_denied", + "input": "complete_synthetic_manifest", + "mutations": [ + { + "path": "scope.exclusions.deny_if_rule_source_unavailable", + "value": false + } + ], + "expected_disposition": "hold", + "expected_primary_reason": "exclusion_source_outage_must_deny" + }, + { + "case_id": "support_timezone_missing", + "input": "complete_synthetic_manifest", + "mutations": [ + { + "path": "support_coverage.timezone", + "value": null + } + ], + "expected_disposition": "hold", + "expected_primary_reason": "support_timezone_missing_or_invalid" + }, + { + "case_id": "support_windows_empty", + "input": "complete_synthetic_manifest", + "mutations": [ + { + "path": "support_coverage.weekly_windows", + "value": [] + } + ], + "expected_disposition": "hold", + "expected_primary_reason": "support_windows_missing_or_invalid" + }, + { + "case_id": "support_windows_overlap", + "input": "complete_synthetic_manifest", + "mutations": [ + { + "path": "support_coverage.weekly_windows", + "value": [ + { + "iso_weekday": 1, + "starts_at_local": "08:00", + "ends_at_local": "18:00" + }, + { + "iso_weekday": 1, + "starts_at_local": "17:00", + "ends_at_local": "20:00" + } + ] + } + ], + "expected_disposition": "hold", + "expected_primary_reason": "support_windows_overlap" + }, + { + "case_id": "urgent_help_release_missing", + "input": "complete_synthetic_manifest", + "mutations": [ + { + "path": "support_coverage.urgent_help_content_release_id", + "value": null + } + ], + "expected_disposition": "hold", + "expected_primary_reason": "urgent_help_content_release_missing" + }, + { + "case_id": "validity_not_started", + "input": "complete_synthetic_manifest", + "mutations": [ + { + "path": "validity.starts_at", + "value": "2026-07-28T20:00:00Z" + }, + { + "path": "validity.expires_at", + "value": "2026-08-04T20:00:00Z" + } + ], + "expected_disposition": "hold", + "expected_primary_reason": "manifest_not_yet_effective" + }, + { + "case_id": "validity_expired", + "input": "complete_synthetic_manifest", + "mutations": [ + { + "path": "validity.starts_at", + "value": "2026-07-20T19:00:00Z" + }, + { + "path": "validity.expires_at", + "value": "2026-07-27T19:00:00Z" + } + ], + "expected_disposition": "hold", + "expected_primary_reason": "manifest_expired" + }, + { + "case_id": "validity_exceeds_technical_ceiling", + "input": "complete_synthetic_manifest", + "mutations": [ + { + "path": "validity.expires_at", + "value": "2026-08-03T20:00:01Z" + } + ], + "expected_disposition": "hold", + "expected_primary_reason": "manifest_validity_exceeds_ceiling" + }, + { + "case_id": "audit_sink_missing", + "input": "complete_synthetic_manifest", + "mutations": [ + { + "path": "prerequisites.audit_sink_deployment_record", + "value": null + } + ], + "expected_disposition": "hold", + "expected_primary_reason": "prerequisite_record_missing" + }, + { + "case_id": "audit_not_append_only", + "input": "complete_synthetic_manifest", + "mutations": [ + { + "path": "audit.append_only_required", + "value": false + } + ], + "expected_disposition": "hold", + "expected_primary_reason": "audit_append_only_required" + }, + { + "case_id": "audit_event_inventory_incomplete", + "input": "complete_synthetic_manifest", + "mutations": [ + { + "path": "audit.required_event_types", + "value": [ + "manifest_created", + "manifest_review_requested", + "manifest_approved", + "go_no_go_requested", + "pilot_started", + "pilot_expired", + "pilot_withdrawn", + "kill_switch_invoked" + ] + } + ], + "expected_disposition": "hold", + "expected_primary_reason": "audit_event_inventory_invalid" + }, + { + "case_id": "audit_field_inventory_incomplete", + "input": "complete_synthetic_manifest", + "mutations": [ + { + "path": "audit.required_fields", + "value": [ + "manifest_id", + "manifest_revision", + "event_type", + "occurred_at", + "actor_role", + "actor_reference_hash", + "outcome", + "reason_code" + ] + } + ], + "expected_disposition": "hold", + "expected_primary_reason": "audit_field_inventory_invalid" + }, + { + "case_id": "clinical_content_in_audit_attempted", + "input": "complete_synthetic_manifest", + "mutations": [ + { + "path": "audit.clinical_content_permitted", + "value": true + } + ], + "expected_disposition": "hold", + "expected_primary_reason": "audit_content_must_remain_prohibited" + }, + { + "case_id": "rollback_target_missing", + "input": "complete_synthetic_manifest", + "mutations": [ + { + "path": "rollback.rollback_target_release_id", + "value": null + } + ], + "expected_disposition": "hold", + "expected_primary_reason": "rollback_evidence_missing" + }, + { + "case_id": "kill_switch_not_default_engaged", + "input": "complete_synthetic_manifest", + "mutations": [ + { + "path": "rollback.kill_switch_default_state", + "value": "released" + } + ], + "expected_disposition": "hold", + "expected_primary_reason": "kill_switch_must_default_engaged" + }, + { + "case_id": "named_approval_missing", + "input": "complete_synthetic_manifest", + "mutations": [ + { + "path": "approvals.clinical_safety_owner.approver_subject", + "value": null + } + ], + "expected_disposition": "hold", + "expected_primary_reason": "named_approval_missing" + } + ], + "expected_summary": { + "case_count": 34, + "eligible_for_external_go_no_go_review_only": 1, + "hold": 33 + } +} diff --git a/docs/nightingale/source-candidates/current-inpatient/v0/candidate.json b/docs/nightingale/source-candidates/current-inpatient/v0/candidate.json new file mode 100644 index 00000000..29d0c854 --- /dev/null +++ b/docs/nightingale/source-candidates/current-inpatient/v0/candidate.json @@ -0,0 +1,136 @@ +{ + "artifact_kind": "nightingale-nonrunnable-inpatient-source-candidate", + "candidate_id": "nightingale.current-inpatient-source.v0-candidate", + "status": "held-no-adapter-no-query", + "purpose": "Define fail-closed current-inpatient source states without selecting, binding, or querying a production source.", + "runtime": { + "route": null, + "operation_id": null, + "source_adapter": null, + "query_contract": null, + "database_connection": null, + "service_binding_permitted": false, + "query_implementation_permitted": false, + "production_query_permitted": false + }, + "activation": { + "inpatient_source": false, + "facility_scope": false, + "unit_scope": false, + "patient_class_scope": false, + "nonproduction_integration": false, + "production": false + }, + "source_states": { + "unavailable": { + "meaning": "The source, policy, linkage, clock, audit, or required evidence cannot support a reliable determination.", + "permits_governed_evaluation": false, + "authorizes_patient_access": false + }, + "inconsistent": { + "meaning": "Available source facts contradict one another or cannot be reduced to exactly one coherent lifecycle.", + "permits_governed_evaluation": false, + "authorizes_patient_access": false + }, + "confirmed_closed": { + "meaning": "A governed source evaluation confirms a coherent encounter lifecycle that is no longer current.", + "permits_governed_evaluation": false, + "authorizes_patient_access": false + }, + "confirmed_current": { + "meaning": "A governed source evaluation confirms exactly one current inpatient context at the evaluation instant.", + "permits_governed_evaluation": true, + "authorizes_patient_access": false + } + }, + "freshness_policy": { + "policy_version": null, + "evaluation_clock": null, + "maximum_source_age_seconds": null, + "thresholds_approved": false, + "hard_coded_thresholds_permitted": false + }, + "positive_state_prerequisites": [ + "approved-product-source-and-scope-policy-version", + "approved-source-adapter-and-query-contract", + "source-availability-and-evaluation-clock-confirmed", + "verified-self-principal-linkage-resolved-without-source-id-disclosure", + "facility-unit-and-patient-class-within-approved-scope", + "exactly-one-coherent-current-encounter-lifecycle", + "required-admission-status-transfer-discharge-and-version-facts-complete", + "source-freshness-satisfied-under-approved-versioned-policy", + "no-merge-correction-retraction-or-transfer-transition-unresolved", + "source-record-stable-through-evaluation-handoff", + "durable-source-evaluation-audit-committed", + "identity-grant-purpose-cardinality-and-authorization-still-required" + ], + "closed_state_prerequisites": [ + "all-source-integrity-prerequisites-that-apply-to-a-closed-lifecycle", + "exactly-one-coherent-closed-encounter-lifecycle", + "discharge-finality-confirmed-by-approved-source-policy", + "no-conflicting-active-or-pending-transfer-record", + "durable-source-evaluation-audit-committed" + ], + "identifier_policy": { + "source_identifiers_in_candidate_or_fixtures": "prohibited", + "source_identifiers_in_patient_response": "prohibited", + "opaque_nightingale_handle_generation": "outside-this-candidate", + "cross_product_identifier_reuse": "prohibited" + }, + "required_fixture_case_ids": [ + "confirmed_current_all_prerequisites", + "confirmed_closed_all_prerequisites", + "product_disabled", + "source_capability_disabled", + "source_adapter_unconfigured", + "source_dependency_unavailable", + "source_timeout", + "database_unavailable", + "source_response_malformed", + "source_policy_unconfigured", + "freshness_policy_unapproved", + "source_observed_at_missing", + "source_observed_at_stale", + "clock_untrusted", + "no_inpatient_record", + "patient_link_missing", + "patient_link_unverified", + "facility_out_of_scope", + "unit_out_of_scope", + "patient_class_out_of_scope", + "cohort_policy_unavailable", + "required_field_missing", + "source_audit_unavailable", + "multiple_current_records", + "duplicate_source_link", + "ambiguous_patient_match", + "status_missing", + "status_unknown", + "record_soft_deleted", + "active_with_discharge_time", + "admitted_at_missing", + "admitted_at_future", + "discharged_at_missing", + "discharged_at_future", + "discharged_before_admitted", + "lifecycle_inconsistent", + "source_version_missing", + "source_changed_during_evaluation", + "transfer_unresolved", + "merge_or_correction_pending", + "record_retracted", + "policy_version_mismatch" + ], + "approval_gates": [ + "named-source-clinical-operations-identity-privacy-security-data-governance-support-and-release-owners", + "authoritative-source-and-query-contract", + "facility-unit-patient-class-and-cohort-policy", + "admission-transfer-discharge-and-closure-semantics", + "freshness-clock-time-zone-and-downtime-policy", + "patient-linkage-merge-correction-and-retraction-policy", + "source-identifier-containment-and-opaque-handle-design", + "authorization-nondisclosure-audit-and-race-tests", + "synthetic-and-approved-deidentified-nonproduction-validation", + "default-off-integration-observability-rollback-and-support-plan" + ] +} diff --git a/docs/nightingale/source-candidates/current-inpatient/v0/fixtures.json b/docs/nightingale/source-candidates/current-inpatient/v0/fixtures.json new file mode 100644 index 00000000..d281fad6 --- /dev/null +++ b/docs/nightingale/source-candidates/current-inpatient/v0/fixtures.json @@ -0,0 +1,404 @@ +{ + "fixture_schema": "nightingale.current-inpatient-source.candidate-fixtures.v0", + "candidate_id": "nightingale.current-inpatient-source.v0-candidate", + "synthetic_only": true, + "production_replay_permitted": false, + "contains_credentials": false, + "contains_source_identifiers": false, + "result_templates": { + "unavailable": { + "source_state": "unavailable", + "permits_governed_evaluation": false, + "authorizes_patient_access": false + }, + "inconsistent": { + "source_state": "inconsistent", + "permits_governed_evaluation": false, + "authorizes_patient_access": false + }, + "confirmed_closed": { + "source_state": "confirmed_closed", + "permits_governed_evaluation": false, + "authorizes_patient_access": false + }, + "confirmed_current": { + "source_state": "confirmed_current", + "permits_governed_evaluation": true, + "authorizes_patient_access": false + } + }, + "cases": [ + { + "case_id": "confirmed_current_all_prerequisites", + "category": "positive", + "preconditions": [ + "Every confirmed-current prerequisite is satisfied for exactly one coherent inpatient lifecycle." + ], + "expected_result": "confirmed_current", + "audit_mode": "durable_evaluation" + }, + { + "case_id": "confirmed_closed_all_prerequisites", + "category": "closed", + "preconditions": [ + "Every confirmed-closed prerequisite is satisfied for exactly one coherent closed lifecycle." + ], + "expected_result": "confirmed_closed", + "audit_mode": "durable_evaluation" + }, + { + "case_id": "product_disabled", + "category": "activation", + "preconditions": ["The Nightingale product gate is disabled."], + "expected_result": "unavailable", + "audit_mode": "best_effort_unavailable" + }, + { + "case_id": "source_capability_disabled", + "category": "activation", + "preconditions": ["The inpatient-source capability is disabled."], + "expected_result": "unavailable", + "audit_mode": "best_effort_unavailable" + }, + { + "case_id": "source_adapter_unconfigured", + "category": "dependency", + "preconditions": [ + "No approved inpatient-source adapter is configured." + ], + "expected_result": "unavailable", + "audit_mode": "best_effort_unavailable" + }, + { + "case_id": "source_dependency_unavailable", + "category": "dependency", + "preconditions": [ + "The approved source dependency cannot be reached." + ], + "expected_result": "unavailable", + "audit_mode": "best_effort_unavailable" + }, + { + "case_id": "source_timeout", + "category": "dependency", + "preconditions": [ + "Source evaluation does not complete within its governed deadline." + ], + "expected_result": "unavailable", + "audit_mode": "best_effort_unavailable" + }, + { + "case_id": "database_unavailable", + "category": "dependency", + "preconditions": [ + "The future approved source database is unavailable." + ], + "expected_result": "unavailable", + "audit_mode": "best_effort_unavailable" + }, + { + "case_id": "source_response_malformed", + "category": "integrity", + "preconditions": [ + "Source evidence cannot be parsed or validated exactly." + ], + "expected_result": "unavailable", + "audit_mode": "durable_safety_failure" + }, + { + "case_id": "source_policy_unconfigured", + "category": "policy", + "preconditions": [ + "No approved, versioned current-inpatient policy is configured." + ], + "expected_result": "unavailable", + "audit_mode": "durable_safety_failure" + }, + { + "case_id": "freshness_policy_unapproved", + "category": "freshness", + "preconditions": [ + "A source age exists but no approved threshold can interpret it." + ], + "expected_result": "unavailable", + "audit_mode": "durable_safety_failure" + }, + { + "case_id": "source_observed_at_missing", + "category": "freshness", + "preconditions": [ + "The source observation time required by policy is missing." + ], + "expected_result": "unavailable", + "audit_mode": "durable_safety_failure" + }, + { + "case_id": "source_observed_at_stale", + "category": "freshness", + "preconditions": [ + "The source observation is stale under the approved policy version." + ], + "expected_result": "unavailable", + "audit_mode": "durable_safety_failure" + }, + { + "case_id": "clock_untrusted", + "category": "freshness", + "preconditions": [ + "The evaluation clock or time-zone interpretation cannot be trusted." + ], + "expected_result": "unavailable", + "audit_mode": "durable_safety_failure" + }, + { + "case_id": "no_inpatient_record", + "category": "absence", + "preconditions": [ + "The source returns no record that can establish current or closed inpatient state." + ], + "expected_result": "unavailable", + "audit_mode": "durable_evaluation" + }, + { + "case_id": "patient_link_missing", + "category": "linkage", + "preconditions": [ + "No governed patient-to-source linkage is available." + ], + "expected_result": "unavailable", + "audit_mode": "durable_safety_failure" + }, + { + "case_id": "patient_link_unverified", + "category": "linkage", + "preconditions": [ + "The patient-to-source linkage is pending or unverified." + ], + "expected_result": "unavailable", + "audit_mode": "durable_safety_failure" + }, + { + "case_id": "facility_out_of_scope", + "category": "scope", + "preconditions": [ + "The facility is not in the approved Nightingale source cohort." + ], + "expected_result": "unavailable", + "audit_mode": "durable_evaluation" + }, + { + "case_id": "unit_out_of_scope", + "category": "scope", + "preconditions": [ + "The unit is not in the approved Nightingale source cohort." + ], + "expected_result": "unavailable", + "audit_mode": "durable_evaluation" + }, + { + "case_id": "patient_class_out_of_scope", + "category": "scope", + "preconditions": [ + "The patient class is not approved as an inpatient cohort." + ], + "expected_result": "unavailable", + "audit_mode": "durable_evaluation" + }, + { + "case_id": "cohort_policy_unavailable", + "category": "scope", + "preconditions": [ + "Facility, unit, or patient-class scope policy cannot be evaluated." + ], + "expected_result": "unavailable", + "audit_mode": "durable_safety_failure" + }, + { + "case_id": "required_field_missing", + "category": "completeness", + "preconditions": ["A policy-required source fact is absent."], + "expected_result": "unavailable", + "audit_mode": "durable_safety_failure" + }, + { + "case_id": "source_audit_unavailable", + "category": "audit", + "preconditions": [ + "The durable source-evaluation audit cannot be committed." + ], + "expected_result": "unavailable", + "audit_mode": "best_effort_safety_failure" + }, + { + "case_id": "multiple_current_records", + "category": "cardinality", + "preconditions": [ + "More than one current inpatient lifecycle is eligible." + ], + "expected_result": "inconsistent", + "audit_mode": "durable_safety_failure" + }, + { + "case_id": "duplicate_source_link", + "category": "linkage", + "preconditions": [ + "More than one active source linkage claims the same patient context." + ], + "expected_result": "inconsistent", + "audit_mode": "durable_safety_failure" + }, + { + "case_id": "ambiguous_patient_match", + "category": "linkage", + "preconditions": [ + "Source evidence maps to more than one possible patient." + ], + "expected_result": "inconsistent", + "audit_mode": "durable_safety_failure" + }, + { + "case_id": "status_missing", + "category": "lifecycle", + "preconditions": [ + "The source status required to classify the lifecycle is missing." + ], + "expected_result": "inconsistent", + "audit_mode": "durable_safety_failure" + }, + { + "case_id": "status_unknown", + "category": "lifecycle", + "preconditions": [ + "The source status is not recognized by the approved mapping." + ], + "expected_result": "inconsistent", + "audit_mode": "durable_safety_failure" + }, + { + "case_id": "record_soft_deleted", + "category": "lifecycle", + "preconditions": [ + "The only apparent current record is marked deleted or void." + ], + "expected_result": "inconsistent", + "audit_mode": "durable_safety_failure" + }, + { + "case_id": "active_with_discharge_time", + "category": "lifecycle", + "preconditions": [ + "The source claims active status while also recording a final discharge time." + ], + "expected_result": "inconsistent", + "audit_mode": "durable_safety_failure" + }, + { + "case_id": "admitted_at_missing", + "category": "lifecycle", + "preconditions": [ + "A supposedly current or closed encounter lacks the required admission instant." + ], + "expected_result": "inconsistent", + "audit_mode": "durable_safety_failure" + }, + { + "case_id": "admitted_at_future", + "category": "lifecycle", + "preconditions": [ + "The admission instant is after the trusted evaluation instant." + ], + "expected_result": "inconsistent", + "audit_mode": "durable_safety_failure" + }, + { + "case_id": "discharged_at_missing", + "category": "lifecycle", + "preconditions": [ + "The source claims a closed lifecycle without a required final discharge instant." + ], + "expected_result": "inconsistent", + "audit_mode": "durable_safety_failure" + }, + { + "case_id": "discharged_at_future", + "category": "lifecycle", + "preconditions": [ + "A final discharge instant is after the trusted evaluation instant." + ], + "expected_result": "inconsistent", + "audit_mode": "durable_safety_failure" + }, + { + "case_id": "discharged_before_admitted", + "category": "lifecycle", + "preconditions": [ + "The final discharge instant precedes the admission instant." + ], + "expected_result": "inconsistent", + "audit_mode": "durable_safety_failure" + }, + { + "case_id": "lifecycle_inconsistent", + "category": "lifecycle", + "preconditions": [ + "Admission, transfer, status, or discharge facts form no coherent lifecycle." + ], + "expected_result": "inconsistent", + "audit_mode": "durable_safety_failure" + }, + { + "case_id": "source_version_missing", + "category": "concurrency", + "preconditions": [ + "No source version or equivalent stability evidence is available." + ], + "expected_result": "inconsistent", + "audit_mode": "durable_safety_failure" + }, + { + "case_id": "source_changed_during_evaluation", + "category": "concurrency", + "preconditions": [ + "The source lifecycle changes before governed handoff completes." + ], + "expected_result": "inconsistent", + "audit_mode": "durable_safety_failure" + }, + { + "case_id": "transfer_unresolved", + "category": "transition", + "preconditions": [ + "A pending transfer leaves current facility or unit state unresolved." + ], + "expected_result": "inconsistent", + "audit_mode": "durable_safety_failure" + }, + { + "case_id": "merge_or_correction_pending", + "category": "transition", + "preconditions": [ + "A merge or correction can change patient linkage or lifecycle classification." + ], + "expected_result": "inconsistent", + "audit_mode": "durable_safety_failure" + }, + { + "case_id": "record_retracted", + "category": "transition", + "preconditions": [ + "The source retracts evidence previously used to classify the lifecycle." + ], + "expected_result": "inconsistent", + "audit_mode": "durable_safety_failure" + }, + { + "case_id": "policy_version_mismatch", + "category": "policy", + "preconditions": [ + "Evaluation facts and handoff use different source policy versions." + ], + "expected_result": "inconsistent", + "audit_mode": "durable_safety_failure" + } + ] +} diff --git a/docs/nightingale/supply-chain/foundation-dependency-inventory.v0.json b/docs/nightingale/supply-chain/foundation-dependency-inventory.v0.json new file mode 100644 index 00000000..db121940 --- /dev/null +++ b/docs/nightingale/supply-chain/foundation-dependency-inventory.v0.json @@ -0,0 +1,2838 @@ +{ + "schema": "net.acumenus.nightingale.foundation-dependency-inventory", + "schema_version": 1, + "product": { + "name": "Nightingale", + "audience": "inpatients and permitted representatives", + "apple_bundle_id": "net.acumenus.nightingale", + "android_application_id": "net.acumenus.nightingale", + "release_state": "offline foundation; not approved for distribution or live use" + }, + "scope": { + "inventory_kind": "governed foundation dependency inventory", + "android_configuration": "releaseRuntimeClasspath", + "ios_configuration": "Nightingale application target declarations", + "includes": [ + "Android direct Release runtime declarations", + "Android resolved external Release runtime components", + "Android resolved dependency edges", + "iOS application-target third-party package declarations", + "iOS application-target Apple system-module imports", + "declared native build requirements" + ], + "excludes": [ + "licenses and legal conclusions", + "vulnerability status or exploitability", + "artifact checksums and signing provenance", + "package-registry provenance and source-repository identity", + "resolved build-plugin and build-tool transitive dependencies", + "build-host, compiler, SDK-image, and operating-system contents", + "test-only and debug-only dependency resolution", + "CycloneDX, SPDX, or other standards conformance" + ], + "contains_patient_data": false, + "production_system_access_used": false + }, + "generation": { + "command": "JAVA_HOME= node scripts/ci/generate-nightingale-foundation-dependency-inventory.mjs .", + "gradle_task": ":app:writeNightingaleReleaseDependencyResolution", + "deterministic_timestamp_policy": "No generation timestamp is stored; source hashes define inventory identity.", + "source_sha256": { + "nightingale/androidApp/app/build.gradle.kts": "d3a333b3f397b71fbd37647fcf8878f85e75a4e2fa09f9b109448fac86707ab6", + "nightingale/androidApp/build.gradle.kts": "a2c1a3dc7e80664eeede26bfe0b554c1afbbfe81b9b5dfbc33e6aa3fcee3ee1e", + "nightingale/androidApp/settings.gradle.kts": "6746ef09487ea290564b04c8836f9c3e074fd470160598a19fbf32efaf333b82", + "nightingale/androidApp/gradle/wrapper/gradle-wrapper.properties": "ad8aae83783da405b1acb1d39069787f8de5fd64ae5dbf1480f8ced80956c433", + "nightingale/iosApp/project.yml": "9eaa4650ee985b80661d32d206ec71d2d2a1b44d87e430d48343d22f35f7cb85", + "scripts/ci/generate-nightingale-foundation-dependency-inventory.mjs": "42cd775a3d50fa0ac533f71d80aaad631dbc5fa8c0dee8a5f13e21d6e138fed3" + } + }, + "build_requirements": { + "android": { + "gradle_wrapper": "8.11.1", + "android_gradle_plugin": "8.7.3", + "kotlin_plugins": "2.1.20", + "java_source_and_target": "17", + "compile_sdk": 35, + "min_sdk": 26, + "target_sdk": 35 + }, + "ios": { + "xcodegen_spec": "nightingale/iosApp/project.yml", + "swift_language_version": "5.0", + "minimum_ios": "17.0", + "third_party_package_manager": "none declared" + } + }, + "android": { + "configuration": "releaseRuntimeClasspath", + "declared_dependencies": [ + { + "group": "androidx.activity", + "module": "activity-compose", + "requested_version": "1.10.0" + }, + { + "group": "androidx.compose", + "module": "compose-bom", + "requested_version": "2025.02.00" + }, + { + "group": "androidx.compose.animation", + "module": "animation-core", + "requested_version": null + }, + { + "group": "androidx.compose.foundation", + "module": "foundation", + "requested_version": null + }, + { + "group": "androidx.compose.material3", + "module": "material3", + "requested_version": null + }, + { + "group": "androidx.compose.ui", + "module": "ui", + "requested_version": null + }, + { + "group": "androidx.compose.ui", + "module": "ui-tooling-preview", + "requested_version": null + } + ], + "resolved_components": [ + { + "group": "androidx.activity", + "module": "activity", + "version": "1.10.0" + }, + { + "group": "androidx.activity", + "module": "activity-compose", + "version": "1.10.0" + }, + { + "group": "androidx.activity", + "module": "activity-ktx", + "version": "1.10.0" + }, + { + "group": "androidx.annotation", + "module": "annotation", + "version": "1.8.1" + }, + { + "group": "androidx.annotation", + "module": "annotation-experimental", + "version": "1.4.1" + }, + { + "group": "androidx.annotation", + "module": "annotation-jvm", + "version": "1.8.1" + }, + { + "group": "androidx.arch.core", + "module": "core-common", + "version": "2.2.0" + }, + { + "group": "androidx.arch.core", + "module": "core-runtime", + "version": "2.2.0" + }, + { + "group": "androidx.autofill", + "module": "autofill", + "version": "1.0.0" + }, + { + "group": "androidx.collection", + "module": "collection", + "version": "1.4.4" + }, + { + "group": "androidx.collection", + "module": "collection-jvm", + "version": "1.4.4" + }, + { + "group": "androidx.collection", + "module": "collection-ktx", + "version": "1.4.4" + }, + { + "group": "androidx.compose", + "module": "compose-bom", + "version": "2025.02.00" + }, + { + "group": "androidx.compose.animation", + "module": "animation", + "version": "1.7.8" + }, + { + "group": "androidx.compose.animation", + "module": "animation-android", + "version": "1.7.8" + }, + { + "group": "androidx.compose.animation", + "module": "animation-core", + "version": "1.7.8" + }, + { + "group": "androidx.compose.animation", + "module": "animation-core-android", + "version": "1.7.8" + }, + { + "group": "androidx.compose.foundation", + "module": "foundation", + "version": "1.7.8" + }, + { + "group": "androidx.compose.foundation", + "module": "foundation-android", + "version": "1.7.8" + }, + { + "group": "androidx.compose.foundation", + "module": "foundation-layout", + "version": "1.7.8" + }, + { + "group": "androidx.compose.foundation", + "module": "foundation-layout-android", + "version": "1.7.8" + }, + { + "group": "androidx.compose.material", + "module": "material-icons-core", + "version": "1.7.8" + }, + { + "group": "androidx.compose.material", + "module": "material-icons-core-android", + "version": "1.7.8" + }, + { + "group": "androidx.compose.material", + "module": "material-ripple", + "version": "1.7.8" + }, + { + "group": "androidx.compose.material", + "module": "material-ripple-android", + "version": "1.7.8" + }, + { + "group": "androidx.compose.material3", + "module": "material3", + "version": "1.3.1" + }, + { + "group": "androidx.compose.material3", + "module": "material3-android", + "version": "1.3.1" + }, + { + "group": "androidx.compose.runtime", + "module": "runtime", + "version": "1.7.8" + }, + { + "group": "androidx.compose.runtime", + "module": "runtime-android", + "version": "1.7.8" + }, + { + "group": "androidx.compose.runtime", + "module": "runtime-saveable", + "version": "1.7.8" + }, + { + "group": "androidx.compose.runtime", + "module": "runtime-saveable-android", + "version": "1.7.8" + }, + { + "group": "androidx.compose.ui", + "module": "ui", + "version": "1.7.8" + }, + { + "group": "androidx.compose.ui", + "module": "ui-android", + "version": "1.7.8" + }, + { + "group": "androidx.compose.ui", + "module": "ui-geometry", + "version": "1.7.8" + }, + { + "group": "androidx.compose.ui", + "module": "ui-geometry-android", + "version": "1.7.8" + }, + { + "group": "androidx.compose.ui", + "module": "ui-graphics", + "version": "1.7.8" + }, + { + "group": "androidx.compose.ui", + "module": "ui-graphics-android", + "version": "1.7.8" + }, + { + "group": "androidx.compose.ui", + "module": "ui-text", + "version": "1.7.8" + }, + { + "group": "androidx.compose.ui", + "module": "ui-text-android", + "version": "1.7.8" + }, + { + "group": "androidx.compose.ui", + "module": "ui-tooling-preview", + "version": "1.7.8" + }, + { + "group": "androidx.compose.ui", + "module": "ui-tooling-preview-android", + "version": "1.7.8" + }, + { + "group": "androidx.compose.ui", + "module": "ui-unit", + "version": "1.7.8" + }, + { + "group": "androidx.compose.ui", + "module": "ui-unit-android", + "version": "1.7.8" + }, + { + "group": "androidx.compose.ui", + "module": "ui-util", + "version": "1.7.8" + }, + { + "group": "androidx.compose.ui", + "module": "ui-util-android", + "version": "1.7.8" + }, + { + "group": "androidx.concurrent", + "module": "concurrent-futures", + "version": "1.1.0" + }, + { + "group": "androidx.core", + "module": "core", + "version": "1.13.1" + }, + { + "group": "androidx.core", + "module": "core-ktx", + "version": "1.13.1" + }, + { + "group": "androidx.customview", + "module": "customview-poolingcontainer", + "version": "1.0.0" + }, + { + "group": "androidx.emoji2", + "module": "emoji2", + "version": "1.3.0" + }, + { + "group": "androidx.graphics", + "module": "graphics-path", + "version": "1.0.1" + }, + { + "group": "androidx.interpolator", + "module": "interpolator", + "version": "1.0.0" + }, + { + "group": "androidx.lifecycle", + "module": "lifecycle-common", + "version": "2.8.3" + }, + { + "group": "androidx.lifecycle", + "module": "lifecycle-common-java8", + "version": "2.8.3" + }, + { + "group": "androidx.lifecycle", + "module": "lifecycle-common-jvm", + "version": "2.8.3" + }, + { + "group": "androidx.lifecycle", + "module": "lifecycle-livedata-core", + "version": "2.8.3" + }, + { + "group": "androidx.lifecycle", + "module": "lifecycle-process", + "version": "2.8.3" + }, + { + "group": "androidx.lifecycle", + "module": "lifecycle-runtime", + "version": "2.8.3" + }, + { + "group": "androidx.lifecycle", + "module": "lifecycle-runtime-android", + "version": "2.8.3" + }, + { + "group": "androidx.lifecycle", + "module": "lifecycle-runtime-compose", + "version": "2.8.3" + }, + { + "group": "androidx.lifecycle", + "module": "lifecycle-runtime-compose-android", + "version": "2.8.3" + }, + { + "group": "androidx.lifecycle", + "module": "lifecycle-runtime-ktx", + "version": "2.8.3" + }, + { + "group": "androidx.lifecycle", + "module": "lifecycle-runtime-ktx-android", + "version": "2.8.3" + }, + { + "group": "androidx.lifecycle", + "module": "lifecycle-viewmodel", + "version": "2.8.3" + }, + { + "group": "androidx.lifecycle", + "module": "lifecycle-viewmodel-android", + "version": "2.8.3" + }, + { + "group": "androidx.lifecycle", + "module": "lifecycle-viewmodel-ktx", + "version": "2.8.3" + }, + { + "group": "androidx.lifecycle", + "module": "lifecycle-viewmodel-savedstate", + "version": "2.8.3" + }, + { + "group": "androidx.profileinstaller", + "module": "profileinstaller", + "version": "1.4.0" + }, + { + "group": "androidx.savedstate", + "module": "savedstate", + "version": "1.2.1" + }, + { + "group": "androidx.savedstate", + "module": "savedstate-ktx", + "version": "1.2.1" + }, + { + "group": "androidx.startup", + "module": "startup-runtime", + "version": "1.1.1" + }, + { + "group": "androidx.tracing", + "module": "tracing", + "version": "1.0.0" + }, + { + "group": "androidx.versionedparcelable", + "module": "versionedparcelable", + "version": "1.1.1" + }, + { + "group": "com.google.guava", + "module": "listenablefuture", + "version": "1.0" + }, + { + "group": "org.jetbrains", + "module": "annotations", + "version": "23.0.0" + }, + { + "group": "org.jetbrains.kotlin", + "module": "kotlin-stdlib", + "version": "2.1.20" + }, + { + "group": "org.jetbrains.kotlin", + "module": "kotlin-stdlib-common", + "version": "2.1.20" + }, + { + "group": "org.jetbrains.kotlin", + "module": "kotlin-stdlib-jdk7", + "version": "1.8.20" + }, + { + "group": "org.jetbrains.kotlin", + "module": "kotlin-stdlib-jdk8", + "version": "1.8.20" + }, + { + "group": "org.jetbrains.kotlinx", + "module": "kotlinx-coroutines-android", + "version": "1.7.3" + }, + { + "group": "org.jetbrains.kotlinx", + "module": "kotlinx-coroutines-bom", + "version": "1.7.3" + }, + { + "group": "org.jetbrains.kotlinx", + "module": "kotlinx-coroutines-core", + "version": "1.7.3" + }, + { + "group": "org.jetbrains.kotlinx", + "module": "kotlinx-coroutines-core-jvm", + "version": "1.7.3" + } + ], + "dependency_edges": [ + { + "from": "androidx.activity:activity-compose:1.10.0", + "requested": "androidx.activity:activity-ktx:1.10.0", + "selected": "androidx.activity:activity-ktx:1.10.0" + }, + { + "from": "androidx.activity:activity-compose:1.10.0", + "requested": "androidx.activity:activity:1.10.0", + "selected": "androidx.activity:activity:1.10.0" + }, + { + "from": "androidx.activity:activity-compose:1.10.0", + "requested": "androidx.compose.runtime:runtime-saveable:1.7.0", + "selected": "androidx.compose.runtime:runtime-saveable:1.7.8" + }, + { + "from": "androidx.activity:activity-compose:1.10.0", + "requested": "androidx.compose.runtime:runtime:1.7.0", + "selected": "androidx.compose.runtime:runtime:1.7.8" + }, + { + "from": "androidx.activity:activity-compose:1.10.0", + "requested": "androidx.compose.ui:ui:1.0.1", + "selected": "androidx.compose.ui:ui:1.7.8" + }, + { + "from": "androidx.activity:activity-compose:1.10.0", + "requested": "androidx.core:core-ktx:1.13.0", + "selected": "androidx.core:core-ktx:1.13.1" + }, + { + "from": "androidx.activity:activity-compose:1.10.0", + "requested": "androidx.lifecycle:lifecycle-common:2.6.1", + "selected": "androidx.lifecycle:lifecycle-common:2.8.3" + }, + { + "from": "androidx.activity:activity-compose:1.10.0", + "requested": "androidx.lifecycle:lifecycle-runtime:2.6.1", + "selected": "androidx.lifecycle:lifecycle-runtime:2.8.3" + }, + { + "from": "androidx.activity:activity-compose:1.10.0", + "requested": "androidx.lifecycle:lifecycle-viewmodel:2.6.1", + "selected": "androidx.lifecycle:lifecycle-viewmodel:2.8.3" + }, + { + "from": "androidx.activity:activity-compose:1.10.0", + "requested": "androidx.savedstate:savedstate:1.2.1", + "selected": "androidx.savedstate:savedstate:1.2.1" + }, + { + "from": "androidx.activity:activity-compose:1.10.0", + "requested": "org.jetbrains.kotlin:kotlin-stdlib", + "selected": "org.jetbrains.kotlin:kotlin-stdlib:2.1.20" + }, + { + "from": "androidx.activity:activity-compose:1.10.0", + "requested": "org.jetbrains.kotlin:kotlin-stdlib:1.8.22", + "selected": "org.jetbrains.kotlin:kotlin-stdlib:2.1.20" + }, + { + "from": "androidx.activity:activity-compose:1.10.0", + "requested": "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.7.3", + "selected": "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.7.3" + }, + { + "from": "androidx.activity:activity-ktx:1.10.0", + "requested": "androidx.activity:activity-compose:1.10.0", + "selected": "androidx.activity:activity-compose:1.10.0" + }, + { + "from": "androidx.activity:activity-ktx:1.10.0", + "requested": "androidx.activity:activity:1.10.0", + "selected": "androidx.activity:activity:1.10.0" + }, + { + "from": "androidx.activity:activity-ktx:1.10.0", + "requested": "androidx.lifecycle:lifecycle-runtime-ktx:2.6.1", + "selected": "androidx.lifecycle:lifecycle-runtime-ktx:2.8.3" + }, + { + "from": "androidx.activity:activity-ktx:1.10.0", + "requested": "androidx.lifecycle:lifecycle-viewmodel-ktx:2.6.1", + "selected": "androidx.lifecycle:lifecycle-viewmodel-ktx:2.8.3" + }, + { + "from": "androidx.activity:activity-ktx:1.10.0", + "requested": "androidx.savedstate:savedstate-ktx:1.2.1", + "selected": "androidx.savedstate:savedstate-ktx:1.2.1" + }, + { + "from": "androidx.activity:activity:1.10.0", + "requested": "androidx.activity:activity-compose:1.10.0", + "selected": "androidx.activity:activity-compose:1.10.0" + }, + { + "from": "androidx.activity:activity:1.10.0", + "requested": "androidx.activity:activity-ktx:1.10.0", + "selected": "androidx.activity:activity-ktx:1.10.0" + }, + { + "from": "androidx.activity:activity:1.10.0", + "requested": "androidx.annotation:annotation:1.8.1", + "selected": "androidx.annotation:annotation:1.8.1" + }, + { + "from": "androidx.activity:activity:1.10.0", + "requested": "androidx.core:core-ktx:1.13.0", + "selected": "androidx.core:core-ktx:1.13.1" + }, + { + "from": "androidx.activity:activity:1.10.0", + "requested": "androidx.lifecycle:lifecycle-common:2.6.1", + "selected": "androidx.lifecycle:lifecycle-common:2.8.3" + }, + { + "from": "androidx.activity:activity:1.10.0", + "requested": "androidx.lifecycle:lifecycle-runtime:2.6.1", + "selected": "androidx.lifecycle:lifecycle-runtime:2.8.3" + }, + { + "from": "androidx.activity:activity:1.10.0", + "requested": "androidx.lifecycle:lifecycle-viewmodel-savedstate:2.6.1", + "selected": "androidx.lifecycle:lifecycle-viewmodel-savedstate:2.8.3" + }, + { + "from": "androidx.activity:activity:1.10.0", + "requested": "androidx.lifecycle:lifecycle-viewmodel:2.6.1", + "selected": "androidx.lifecycle:lifecycle-viewmodel:2.8.3" + }, + { + "from": "androidx.activity:activity:1.10.0", + "requested": "androidx.profileinstaller:profileinstaller:1.4.0", + "selected": "androidx.profileinstaller:profileinstaller:1.4.0" + }, + { + "from": "androidx.activity:activity:1.10.0", + "requested": "androidx.savedstate:savedstate:1.2.1", + "selected": "androidx.savedstate:savedstate:1.2.1" + }, + { + "from": "androidx.activity:activity:1.10.0", + "requested": "androidx.tracing:tracing:1.0.0", + "selected": "androidx.tracing:tracing:1.0.0" + }, + { + "from": "androidx.activity:activity:1.10.0", + "requested": "org.jetbrains.kotlin:kotlin-stdlib", + "selected": "org.jetbrains.kotlin:kotlin-stdlib:2.1.20" + }, + { + "from": "androidx.activity:activity:1.10.0", + "requested": "org.jetbrains.kotlin:kotlin-stdlib:1.8.22", + "selected": "org.jetbrains.kotlin:kotlin-stdlib:2.1.20" + }, + { + "from": "androidx.activity:activity:1.10.0", + "requested": "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.7.3", + "selected": "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.7.3" + }, + { + "from": "androidx.annotation:annotation-experimental:1.4.1", + "requested": "org.jetbrains.kotlin:kotlin-stdlib:1.7.10", + "selected": "org.jetbrains.kotlin:kotlin-stdlib:2.1.20" + }, + { + "from": "androidx.annotation:annotation-jvm:1.8.1", + "requested": "org.jetbrains.kotlin:kotlin-stdlib:1.7.10", + "selected": "org.jetbrains.kotlin:kotlin-stdlib:2.1.20" + }, + { + "from": "androidx.annotation:annotation:1.8.1", + "requested": "androidx.annotation:annotation-jvm:1.8.1", + "selected": "androidx.annotation:annotation-jvm:1.8.1" + }, + { + "from": "androidx.arch.core:core-common:2.2.0", + "requested": "androidx.annotation:annotation:1.1.0", + "selected": "androidx.annotation:annotation:1.8.1" + }, + { + "from": "androidx.arch.core:core-runtime:2.2.0", + "requested": "androidx.annotation:annotation:1.1.0", + "selected": "androidx.annotation:annotation:1.8.1" + }, + { + "from": "androidx.arch.core:core-runtime:2.2.0", + "requested": "androidx.arch.core:core-common:2.2.0", + "selected": "androidx.arch.core:core-common:2.2.0" + }, + { + "from": "androidx.autofill:autofill:1.0.0", + "requested": "androidx.core:core:1.1.0", + "selected": "androidx.core:core:1.13.1" + }, + { + "from": "androidx.collection:collection-jvm:1.4.4", + "requested": "androidx.annotation:annotation:1.8.1", + "selected": "androidx.annotation:annotation:1.8.1" + }, + { + "from": "androidx.collection:collection-jvm:1.4.4", + "requested": "androidx.collection:collection-ktx:1.3.0", + "selected": "androidx.collection:collection-ktx:1.4.4" + }, + { + "from": "androidx.collection:collection-jvm:1.4.4", + "requested": "androidx.collection:collection-ktx:1.4.4", + "selected": "androidx.collection:collection-ktx:1.4.4" + }, + { + "from": "androidx.collection:collection-jvm:1.4.4", + "requested": "org.jetbrains.kotlin:kotlin-stdlib:1.8.22", + "selected": "org.jetbrains.kotlin:kotlin-stdlib:2.1.20" + }, + { + "from": "androidx.collection:collection-ktx:1.4.4", + "requested": "androidx.collection:collection:1.4.4", + "selected": "androidx.collection:collection:1.4.4" + }, + { + "from": "androidx.collection:collection:1.4.4", + "requested": "androidx.collection:collection-jvm:1.4.4", + "selected": "androidx.collection:collection-jvm:1.4.4" + }, + { + "from": "androidx.compose.animation:animation-android:1.7.8", + "requested": "androidx.annotation:annotation-experimental:1.4.0", + "selected": "androidx.annotation:annotation-experimental:1.4.1" + }, + { + "from": "androidx.compose.animation:animation-android:1.7.8", + "requested": "androidx.annotation:annotation:1.1.0", + "selected": "androidx.annotation:annotation:1.8.1" + }, + { + "from": "androidx.compose.animation:animation-android:1.7.8", + "requested": "androidx.collection:collection:1.4.0", + "selected": "androidx.collection:collection:1.4.4" + }, + { + "from": "androidx.compose.animation:animation-android:1.7.8", + "requested": "androidx.compose.animation:animation-core:1.7.8", + "selected": "androidx.compose.animation:animation-core:1.7.8" + }, + { + "from": "androidx.compose.animation:animation-android:1.7.8", + "requested": "androidx.compose.foundation:foundation-layout:1.7.8", + "selected": "androidx.compose.foundation:foundation-layout:1.7.8" + }, + { + "from": "androidx.compose.animation:animation-android:1.7.8", + "requested": "androidx.compose.runtime:runtime:1.7.8", + "selected": "androidx.compose.runtime:runtime:1.7.8" + }, + { + "from": "androidx.compose.animation:animation-android:1.7.8", + "requested": "androidx.compose.ui:ui-geometry:1.7.8", + "selected": "androidx.compose.ui:ui-geometry:1.7.8" + }, + { + "from": "androidx.compose.animation:animation-android:1.7.8", + "requested": "androidx.compose.ui:ui-graphics:1.7.8", + "selected": "androidx.compose.ui:ui-graphics:1.7.8" + }, + { + "from": "androidx.compose.animation:animation-android:1.7.8", + "requested": "androidx.compose.ui:ui-util:1.7.8", + "selected": "androidx.compose.ui:ui-util:1.7.8" + }, + { + "from": "androidx.compose.animation:animation-android:1.7.8", + "requested": "androidx.compose.ui:ui:1.7.8", + "selected": "androidx.compose.ui:ui:1.7.8" + }, + { + "from": "androidx.compose.animation:animation-android:1.7.8", + "requested": "org.jetbrains.kotlin:kotlin-stdlib:1.8.22", + "selected": "org.jetbrains.kotlin:kotlin-stdlib:2.1.20" + }, + { + "from": "androidx.compose.animation:animation-core-android:1.7.8", + "requested": "androidx.annotation:annotation:1.1.0", + "selected": "androidx.annotation:annotation:1.8.1" + }, + { + "from": "androidx.compose.animation:animation-core-android:1.7.8", + "requested": "androidx.collection:collection:1.4.0", + "selected": "androidx.collection:collection:1.4.4" + }, + { + "from": "androidx.compose.animation:animation-core-android:1.7.8", + "requested": "androidx.compose.animation:animation:1.7.8", + "selected": "androidx.compose.animation:animation:1.7.8" + }, + { + "from": "androidx.compose.animation:animation-core-android:1.7.8", + "requested": "androidx.compose.runtime:runtime:1.7.8", + "selected": "androidx.compose.runtime:runtime:1.7.8" + }, + { + "from": "androidx.compose.animation:animation-core-android:1.7.8", + "requested": "androidx.compose.ui:ui-graphics:1.7.8", + "selected": "androidx.compose.ui:ui-graphics:1.7.8" + }, + { + "from": "androidx.compose.animation:animation-core-android:1.7.8", + "requested": "androidx.compose.ui:ui-unit:1.7.8", + "selected": "androidx.compose.ui:ui-unit:1.7.8" + }, + { + "from": "androidx.compose.animation:animation-core-android:1.7.8", + "requested": "androidx.compose.ui:ui-util:1.7.8", + "selected": "androidx.compose.ui:ui-util:1.7.8" + }, + { + "from": "androidx.compose.animation:animation-core-android:1.7.8", + "requested": "androidx.compose.ui:ui:1.7.8", + "selected": "androidx.compose.ui:ui:1.7.8" + }, + { + "from": "androidx.compose.animation:animation-core-android:1.7.8", + "requested": "org.jetbrains.kotlin:kotlin-stdlib:1.8.22", + "selected": "org.jetbrains.kotlin:kotlin-stdlib:2.1.20" + }, + { + "from": "androidx.compose.animation:animation-core-android:1.7.8", + "requested": "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.7.3", + "selected": "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.7.3" + }, + { + "from": "androidx.compose.animation:animation-core:1.7.8", + "requested": "androidx.compose.animation:animation-core-android:1.7.8", + "selected": "androidx.compose.animation:animation-core-android:1.7.8" + }, + { + "from": "androidx.compose.animation:animation:1.7.8", + "requested": "androidx.compose.animation:animation-android:1.7.8", + "selected": "androidx.compose.animation:animation-android:1.7.8" + }, + { + "from": "androidx.compose.foundation:foundation-android:1.7.8", + "requested": "androidx.annotation:annotation-experimental:1.4.0", + "selected": "androidx.annotation:annotation-experimental:1.4.1" + }, + { + "from": "androidx.compose.foundation:foundation-android:1.7.8", + "requested": "androidx.annotation:annotation:1.1.0", + "selected": "androidx.annotation:annotation:1.8.1" + }, + { + "from": "androidx.compose.foundation:foundation-android:1.7.8", + "requested": "androidx.collection:collection:1.4.0", + "selected": "androidx.collection:collection:1.4.4" + }, + { + "from": "androidx.compose.foundation:foundation-android:1.7.8", + "requested": "androidx.compose.animation:animation:1.7.8", + "selected": "androidx.compose.animation:animation:1.7.8" + }, + { + "from": "androidx.compose.foundation:foundation-android:1.7.8", + "requested": "androidx.compose.foundation:foundation-layout:1.7.8", + "selected": "androidx.compose.foundation:foundation-layout:1.7.8" + }, + { + "from": "androidx.compose.foundation:foundation-android:1.7.8", + "requested": "androidx.compose.runtime:runtime:1.7.8", + "selected": "androidx.compose.runtime:runtime:1.7.8" + }, + { + "from": "androidx.compose.foundation:foundation-android:1.7.8", + "requested": "androidx.compose.ui:ui-text:1.7.8", + "selected": "androidx.compose.ui:ui-text:1.7.8" + }, + { + "from": "androidx.compose.foundation:foundation-android:1.7.8", + "requested": "androidx.compose.ui:ui-util:1.7.8", + "selected": "androidx.compose.ui:ui-util:1.7.8" + }, + { + "from": "androidx.compose.foundation:foundation-android:1.7.8", + "requested": "androidx.compose.ui:ui:1.7.8", + "selected": "androidx.compose.ui:ui:1.7.8" + }, + { + "from": "androidx.compose.foundation:foundation-android:1.7.8", + "requested": "androidx.core:core:1.13.1", + "selected": "androidx.core:core:1.13.1" + }, + { + "from": "androidx.compose.foundation:foundation-android:1.7.8", + "requested": "androidx.emoji2:emoji2:1.3.0", + "selected": "androidx.emoji2:emoji2:1.3.0" + }, + { + "from": "androidx.compose.foundation:foundation-android:1.7.8", + "requested": "org.jetbrains.kotlin:kotlin-stdlib:1.8.22", + "selected": "org.jetbrains.kotlin:kotlin-stdlib:2.1.20" + }, + { + "from": "androidx.compose.foundation:foundation-layout-android:1.7.8", + "requested": "androidx.annotation:annotation-experimental:1.4.0", + "selected": "androidx.annotation:annotation-experimental:1.4.1" + }, + { + "from": "androidx.compose.foundation:foundation-layout-android:1.7.8", + "requested": "androidx.annotation:annotation:1.1.0", + "selected": "androidx.annotation:annotation:1.8.1" + }, + { + "from": "androidx.compose.foundation:foundation-layout-android:1.7.8", + "requested": "androidx.collection:collection:1.4.0", + "selected": "androidx.collection:collection:1.4.4" + }, + { + "from": "androidx.compose.foundation:foundation-layout-android:1.7.8", + "requested": "androidx.compose.animation:animation-core:1.2.1", + "selected": "androidx.compose.animation:animation-core:1.7.8" + }, + { + "from": "androidx.compose.foundation:foundation-layout-android:1.7.8", + "requested": "androidx.compose.foundation:foundation:1.7.8", + "selected": "androidx.compose.foundation:foundation:1.7.8" + }, + { + "from": "androidx.compose.foundation:foundation-layout-android:1.7.8", + "requested": "androidx.compose.runtime:runtime:1.7.8", + "selected": "androidx.compose.runtime:runtime:1.7.8" + }, + { + "from": "androidx.compose.foundation:foundation-layout-android:1.7.8", + "requested": "androidx.compose.ui:ui-unit:1.7.8", + "selected": "androidx.compose.ui:ui-unit:1.7.8" + }, + { + "from": "androidx.compose.foundation:foundation-layout-android:1.7.8", + "requested": "androidx.compose.ui:ui-util:1.7.8", + "selected": "androidx.compose.ui:ui-util:1.7.8" + }, + { + "from": "androidx.compose.foundation:foundation-layout-android:1.7.8", + "requested": "androidx.compose.ui:ui:1.7.8", + "selected": "androidx.compose.ui:ui:1.7.8" + }, + { + "from": "androidx.compose.foundation:foundation-layout-android:1.7.8", + "requested": "androidx.core:core:1.7.0", + "selected": "androidx.core:core:1.13.1" + }, + { + "from": "androidx.compose.foundation:foundation-layout-android:1.7.8", + "requested": "org.jetbrains.kotlin:kotlin-stdlib:1.8.22", + "selected": "org.jetbrains.kotlin:kotlin-stdlib:2.1.20" + }, + { + "from": "androidx.compose.foundation:foundation-layout:1.7.8", + "requested": "androidx.compose.foundation:foundation-layout-android:1.7.8", + "selected": "androidx.compose.foundation:foundation-layout-android:1.7.8" + }, + { + "from": "androidx.compose.foundation:foundation:1.7.8", + "requested": "androidx.compose.foundation:foundation-android:1.7.8", + "selected": "androidx.compose.foundation:foundation-android:1.7.8" + }, + { + "from": "androidx.compose.material3:material3-android:1.3.1", + "requested": "androidx.activity:activity-compose:1.8.2", + "selected": "androidx.activity:activity-compose:1.10.0" + }, + { + "from": "androidx.compose.material3:material3-android:1.3.1", + "requested": "androidx.annotation:annotation-experimental:1.4.0", + "selected": "androidx.annotation:annotation-experimental:1.4.1" + }, + { + "from": "androidx.compose.material3:material3-android:1.3.1", + "requested": "androidx.annotation:annotation:1.1.0", + "selected": "androidx.annotation:annotation:1.8.1" + }, + { + "from": "androidx.compose.material3:material3-android:1.3.1", + "requested": "androidx.collection:collection:1.4.0", + "selected": "androidx.collection:collection:1.4.4" + }, + { + "from": "androidx.compose.material3:material3-android:1.3.1", + "requested": "androidx.compose.animation:animation-core:1.6.0", + "selected": "androidx.compose.animation:animation-core:1.7.8" + }, + { + "from": "androidx.compose.material3:material3-android:1.3.1", + "requested": "androidx.compose.foundation:foundation-layout:1.7.0", + "selected": "androidx.compose.foundation:foundation-layout:1.7.8" + }, + { + "from": "androidx.compose.material3:material3-android:1.3.1", + "requested": "androidx.compose.foundation:foundation:1.7.0", + "selected": "androidx.compose.foundation:foundation:1.7.8" + }, + { + "from": "androidx.compose.material3:material3-android:1.3.1", + "requested": "androidx.compose.material:material-icons-core:1.6.0", + "selected": "androidx.compose.material:material-icons-core:1.7.8" + }, + { + "from": "androidx.compose.material3:material3-android:1.3.1", + "requested": "androidx.compose.material:material-ripple:1.7.0", + "selected": "androidx.compose.material:material-ripple:1.7.8" + }, + { + "from": "androidx.compose.material3:material3-android:1.3.1", + "requested": "androidx.compose.runtime:runtime:1.7.0", + "selected": "androidx.compose.runtime:runtime:1.7.8" + }, + { + "from": "androidx.compose.material3:material3-android:1.3.1", + "requested": "androidx.compose.ui:ui-text:1.6.0", + "selected": "androidx.compose.ui:ui-text:1.7.8" + }, + { + "from": "androidx.compose.material3:material3-android:1.3.1", + "requested": "androidx.compose.ui:ui-util:1.6.0", + "selected": "androidx.compose.ui:ui-util:1.7.8" + }, + { + "from": "androidx.compose.material3:material3-android:1.3.1", + "requested": "androidx.compose.ui:ui:1.6.0", + "selected": "androidx.compose.ui:ui:1.7.8" + }, + { + "from": "androidx.compose.material3:material3-android:1.3.1", + "requested": "androidx.lifecycle:lifecycle-common-java8:2.6.1", + "selected": "androidx.lifecycle:lifecycle-common-java8:2.8.3" + }, + { + "from": "androidx.compose.material3:material3-android:1.3.1", + "requested": "org.jetbrains.kotlin:kotlin-stdlib-common:1.8.22", + "selected": "org.jetbrains.kotlin:kotlin-stdlib-common:2.1.20" + }, + { + "from": "androidx.compose.material3:material3:1.3.1", + "requested": "androidx.compose.material3:material3-android:1.3.1", + "selected": "androidx.compose.material3:material3-android:1.3.1" + }, + { + "from": "androidx.compose.material:material-icons-core-android:1.7.8", + "requested": "androidx.compose.ui:ui:1.6.0", + "selected": "androidx.compose.ui:ui:1.7.8" + }, + { + "from": "androidx.compose.material:material-icons-core-android:1.7.8", + "requested": "org.jetbrains.kotlin:kotlin-stdlib-common:1.8.22", + "selected": "org.jetbrains.kotlin:kotlin-stdlib-common:2.1.20" + }, + { + "from": "androidx.compose.material:material-icons-core-android:1.7.8", + "requested": "org.jetbrains.kotlin:kotlin-stdlib:1.8.22", + "selected": "org.jetbrains.kotlin:kotlin-stdlib:2.1.20" + }, + { + "from": "androidx.compose.material:material-icons-core:1.7.8", + "requested": "androidx.compose.material:material-icons-core-android:1.7.8", + "selected": "androidx.compose.material:material-icons-core-android:1.7.8" + }, + { + "from": "androidx.compose.material:material-ripple-android:1.7.8", + "requested": "androidx.collection:collection:1.4.0", + "selected": "androidx.collection:collection:1.4.4" + }, + { + "from": "androidx.compose.material:material-ripple-android:1.7.8", + "requested": "androidx.compose.animation:animation:1.7.8", + "selected": "androidx.compose.animation:animation:1.7.8" + }, + { + "from": "androidx.compose.material:material-ripple-android:1.7.8", + "requested": "androidx.compose.foundation:foundation:1.7.8", + "selected": "androidx.compose.foundation:foundation:1.7.8" + }, + { + "from": "androidx.compose.material:material-ripple-android:1.7.8", + "requested": "androidx.compose.runtime:runtime:1.7.8", + "selected": "androidx.compose.runtime:runtime:1.7.8" + }, + { + "from": "androidx.compose.material:material-ripple-android:1.7.8", + "requested": "androidx.compose.ui:ui-util:1.7.8", + "selected": "androidx.compose.ui:ui-util:1.7.8" + }, + { + "from": "androidx.compose.material:material-ripple-android:1.7.8", + "requested": "org.jetbrains.kotlin:kotlin-stdlib:1.8.22", + "selected": "org.jetbrains.kotlin:kotlin-stdlib:2.1.20" + }, + { + "from": "androidx.compose.material:material-ripple:1.7.8", + "requested": "androidx.compose.material:material-ripple-android:1.7.8", + "selected": "androidx.compose.material:material-ripple-android:1.7.8" + }, + { + "from": "androidx.compose.runtime:runtime-android:1.7.8", + "requested": "androidx.annotation:annotation-experimental:1.4.1", + "selected": "androidx.annotation:annotation-experimental:1.4.1" + }, + { + "from": "androidx.compose.runtime:runtime-android:1.7.8", + "requested": "androidx.collection:collection:1.4.4", + "selected": "androidx.collection:collection:1.4.4" + }, + { + "from": "androidx.compose.runtime:runtime-android:1.7.8", + "requested": "androidx.compose.runtime:runtime-saveable:1.7.8", + "selected": "androidx.compose.runtime:runtime-saveable:1.7.8" + }, + { + "from": "androidx.compose.runtime:runtime-android:1.7.8", + "requested": "org.jetbrains.kotlin:kotlin-stdlib:1.8.22", + "selected": "org.jetbrains.kotlin:kotlin-stdlib:2.1.20" + }, + { + "from": "androidx.compose.runtime:runtime-android:1.7.8", + "requested": "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.7.3", + "selected": "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.7.3" + }, + { + "from": "androidx.compose.runtime:runtime-android:1.7.8", + "requested": "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.7.3", + "selected": "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.7.3" + }, + { + "from": "androidx.compose.runtime:runtime-saveable-android:1.7.8", + "requested": "androidx.annotation:annotation:1.1.0", + "selected": "androidx.annotation:annotation:1.8.1" + }, + { + "from": "androidx.compose.runtime:runtime-saveable-android:1.7.8", + "requested": "androidx.collection:collection:1.4.0", + "selected": "androidx.collection:collection:1.4.4" + }, + { + "from": "androidx.compose.runtime:runtime-saveable-android:1.7.8", + "requested": "androidx.compose.runtime:runtime:1.7.8", + "selected": "androidx.compose.runtime:runtime:1.7.8" + }, + { + "from": "androidx.compose.runtime:runtime-saveable-android:1.7.8", + "requested": "org.jetbrains.kotlin:kotlin-stdlib:1.8.22", + "selected": "org.jetbrains.kotlin:kotlin-stdlib:2.1.20" + }, + { + "from": "androidx.compose.runtime:runtime-saveable:1.7.8", + "requested": "androidx.compose.runtime:runtime-saveable-android:1.7.8", + "selected": "androidx.compose.runtime:runtime-saveable-android:1.7.8" + }, + { + "from": "androidx.compose.runtime:runtime:1.7.8", + "requested": "androidx.compose.runtime:runtime-android:1.7.8", + "selected": "androidx.compose.runtime:runtime-android:1.7.8" + }, + { + "from": "androidx.compose.ui:ui-android:1.7.8", + "requested": "androidx.activity:activity-ktx:1.7.0", + "selected": "androidx.activity:activity-ktx:1.10.0" + }, + { + "from": "androidx.compose.ui:ui-android:1.7.8", + "requested": "androidx.annotation:annotation-experimental:1.4.0", + "selected": "androidx.annotation:annotation-experimental:1.4.1" + }, + { + "from": "androidx.compose.ui:ui-android:1.7.8", + "requested": "androidx.annotation:annotation:1.8.0", + "selected": "androidx.annotation:annotation:1.8.1" + }, + { + "from": "androidx.compose.ui:ui-android:1.7.8", + "requested": "androidx.autofill:autofill:1.0.0", + "selected": "androidx.autofill:autofill:1.0.0" + }, + { + "from": "androidx.compose.ui:ui-android:1.7.8", + "requested": "androidx.collection:collection:1.0.0", + "selected": "androidx.collection:collection:1.4.4" + }, + { + "from": "androidx.compose.ui:ui-android:1.7.8", + "requested": "androidx.collection:collection:1.4.0", + "selected": "androidx.collection:collection:1.4.4" + }, + { + "from": "androidx.compose.ui:ui-android:1.7.8", + "requested": "androidx.compose.foundation:foundation:1.7.8", + "selected": "androidx.compose.foundation:foundation:1.7.8" + }, + { + "from": "androidx.compose.ui:ui-android:1.7.8", + "requested": "androidx.compose.runtime:runtime-saveable:1.7.8", + "selected": "androidx.compose.runtime:runtime-saveable:1.7.8" + }, + { + "from": "androidx.compose.ui:ui-android:1.7.8", + "requested": "androidx.compose.runtime:runtime:1.7.8", + "selected": "androidx.compose.runtime:runtime:1.7.8" + }, + { + "from": "androidx.compose.ui:ui-android:1.7.8", + "requested": "androidx.compose.ui:ui-geometry:1.7.8", + "selected": "androidx.compose.ui:ui-geometry:1.7.8" + }, + { + "from": "androidx.compose.ui:ui-android:1.7.8", + "requested": "androidx.compose.ui:ui-graphics:1.7.8", + "selected": "androidx.compose.ui:ui-graphics:1.7.8" + }, + { + "from": "androidx.compose.ui:ui-android:1.7.8", + "requested": "androidx.compose.ui:ui-text:1.7.8", + "selected": "androidx.compose.ui:ui-text:1.7.8" + }, + { + "from": "androidx.compose.ui:ui-android:1.7.8", + "requested": "androidx.compose.ui:ui-tooling-preview:1.7.8", + "selected": "androidx.compose.ui:ui-tooling-preview:1.7.8" + }, + { + "from": "androidx.compose.ui:ui-android:1.7.8", + "requested": "androidx.compose.ui:ui-unit:1.7.8", + "selected": "androidx.compose.ui:ui-unit:1.7.8" + }, + { + "from": "androidx.compose.ui:ui-android:1.7.8", + "requested": "androidx.compose.ui:ui-util:1.7.8", + "selected": "androidx.compose.ui:ui-util:1.7.8" + }, + { + "from": "androidx.compose.ui:ui-android:1.7.8", + "requested": "androidx.core:core:1.12.0", + "selected": "androidx.core:core:1.13.1" + }, + { + "from": "androidx.compose.ui:ui-android:1.7.8", + "requested": "androidx.customview:customview-poolingcontainer:1.0.0", + "selected": "androidx.customview:customview-poolingcontainer:1.0.0" + }, + { + "from": "androidx.compose.ui:ui-android:1.7.8", + "requested": "androidx.emoji2:emoji2:1.2.0", + "selected": "androidx.emoji2:emoji2:1.3.0" + }, + { + "from": "androidx.compose.ui:ui-android:1.7.8", + "requested": "androidx.lifecycle:lifecycle-runtime-compose:2.8.3", + "selected": "androidx.lifecycle:lifecycle-runtime-compose:2.8.3" + }, + { + "from": "androidx.compose.ui:ui-android:1.7.8", + "requested": "androidx.lifecycle:lifecycle-viewmodel:2.6.1", + "selected": "androidx.lifecycle:lifecycle-viewmodel:2.8.3" + }, + { + "from": "androidx.compose.ui:ui-android:1.7.8", + "requested": "androidx.profileinstaller:profileinstaller:1.3.1", + "selected": "androidx.profileinstaller:profileinstaller:1.4.0" + }, + { + "from": "androidx.compose.ui:ui-android:1.7.8", + "requested": "androidx.savedstate:savedstate-ktx:1.2.1", + "selected": "androidx.savedstate:savedstate-ktx:1.2.1" + }, + { + "from": "androidx.compose.ui:ui-android:1.7.8", + "requested": "org.jetbrains.kotlin:kotlin-stdlib:1.8.22", + "selected": "org.jetbrains.kotlin:kotlin-stdlib:2.1.20" + }, + { + "from": "androidx.compose.ui:ui-android:1.7.8", + "requested": "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.7.3", + "selected": "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.7.3" + }, + { + "from": "androidx.compose.ui:ui-android:1.7.8", + "requested": "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.7.3", + "selected": "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.7.3" + }, + { + "from": "androidx.compose.ui:ui-geometry-android:1.7.8", + "requested": "androidx.annotation:annotation:1.1.0", + "selected": "androidx.annotation:annotation:1.8.1" + }, + { + "from": "androidx.compose.ui:ui-geometry-android:1.7.8", + "requested": "androidx.compose.runtime:runtime:1.7.8", + "selected": "androidx.compose.runtime:runtime:1.7.8" + }, + { + "from": "androidx.compose.ui:ui-geometry-android:1.7.8", + "requested": "androidx.compose.ui:ui-graphics:1.7.8", + "selected": "androidx.compose.ui:ui-graphics:1.7.8" + }, + { + "from": "androidx.compose.ui:ui-geometry-android:1.7.8", + "requested": "androidx.compose.ui:ui-text:1.7.8", + "selected": "androidx.compose.ui:ui-text:1.7.8" + }, + { + "from": "androidx.compose.ui:ui-geometry-android:1.7.8", + "requested": "androidx.compose.ui:ui-tooling-preview:1.7.8", + "selected": "androidx.compose.ui:ui-tooling-preview:1.7.8" + }, + { + "from": "androidx.compose.ui:ui-geometry-android:1.7.8", + "requested": "androidx.compose.ui:ui-unit:1.7.8", + "selected": "androidx.compose.ui:ui-unit:1.7.8" + }, + { + "from": "androidx.compose.ui:ui-geometry-android:1.7.8", + "requested": "androidx.compose.ui:ui-util:1.7.8", + "selected": "androidx.compose.ui:ui-util:1.7.8" + }, + { + "from": "androidx.compose.ui:ui-geometry-android:1.7.8", + "requested": "androidx.compose.ui:ui:1.7.8", + "selected": "androidx.compose.ui:ui:1.7.8" + }, + { + "from": "androidx.compose.ui:ui-geometry-android:1.7.8", + "requested": "org.jetbrains.kotlin:kotlin-stdlib:1.8.22", + "selected": "org.jetbrains.kotlin:kotlin-stdlib:2.1.20" + }, + { + "from": "androidx.compose.ui:ui-geometry:1.7.8", + "requested": "androidx.compose.ui:ui-geometry-android:1.7.8", + "selected": "androidx.compose.ui:ui-geometry-android:1.7.8" + }, + { + "from": "androidx.compose.ui:ui-graphics-android:1.7.8", + "requested": "androidx.annotation:annotation-experimental:1.4.0", + "selected": "androidx.annotation:annotation-experimental:1.4.1" + }, + { + "from": "androidx.compose.ui:ui-graphics-android:1.7.8", + "requested": "androidx.annotation:annotation:1.7.0", + "selected": "androidx.annotation:annotation:1.8.1" + }, + { + "from": "androidx.compose.ui:ui-graphics-android:1.7.8", + "requested": "androidx.collection:collection:1.4.0", + "selected": "androidx.collection:collection:1.4.4" + }, + { + "from": "androidx.compose.ui:ui-graphics-android:1.7.8", + "requested": "androidx.compose.runtime:runtime:1.7.8", + "selected": "androidx.compose.runtime:runtime:1.7.8" + }, + { + "from": "androidx.compose.ui:ui-graphics-android:1.7.8", + "requested": "androidx.compose.ui:ui-geometry:1.7.8", + "selected": "androidx.compose.ui:ui-geometry:1.7.8" + }, + { + "from": "androidx.compose.ui:ui-graphics-android:1.7.8", + "requested": "androidx.compose.ui:ui-text:1.7.8", + "selected": "androidx.compose.ui:ui-text:1.7.8" + }, + { + "from": "androidx.compose.ui:ui-graphics-android:1.7.8", + "requested": "androidx.compose.ui:ui-tooling-preview:1.7.8", + "selected": "androidx.compose.ui:ui-tooling-preview:1.7.8" + }, + { + "from": "androidx.compose.ui:ui-graphics-android:1.7.8", + "requested": "androidx.compose.ui:ui-unit:1.7.8", + "selected": "androidx.compose.ui:ui-unit:1.7.8" + }, + { + "from": "androidx.compose.ui:ui-graphics-android:1.7.8", + "requested": "androidx.compose.ui:ui-util:1.7.8", + "selected": "androidx.compose.ui:ui-util:1.7.8" + }, + { + "from": "androidx.compose.ui:ui-graphics-android:1.7.8", + "requested": "androidx.compose.ui:ui:1.7.8", + "selected": "androidx.compose.ui:ui:1.7.8" + }, + { + "from": "androidx.compose.ui:ui-graphics-android:1.7.8", + "requested": "androidx.core:core:1.12.0", + "selected": "androidx.core:core:1.13.1" + }, + { + "from": "androidx.compose.ui:ui-graphics-android:1.7.8", + "requested": "androidx.graphics:graphics-path:1.0.1", + "selected": "androidx.graphics:graphics-path:1.0.1" + }, + { + "from": "androidx.compose.ui:ui-graphics-android:1.7.8", + "requested": "org.jetbrains.kotlin:kotlin-stdlib:1.8.22", + "selected": "org.jetbrains.kotlin:kotlin-stdlib:2.1.20" + }, + { + "from": "androidx.compose.ui:ui-graphics:1.7.8", + "requested": "androidx.compose.ui:ui-graphics-android:1.7.8", + "selected": "androidx.compose.ui:ui-graphics-android:1.7.8" + }, + { + "from": "androidx.compose.ui:ui-text-android:1.7.8", + "requested": "androidx.annotation:annotation-experimental:1.4.0", + "selected": "androidx.annotation:annotation-experimental:1.4.1" + }, + { + "from": "androidx.compose.ui:ui-text-android:1.7.8", + "requested": "androidx.annotation:annotation:1.1.0", + "selected": "androidx.annotation:annotation:1.8.1" + }, + { + "from": "androidx.compose.ui:ui-text-android:1.7.8", + "requested": "androidx.collection:collection:1.4.0", + "selected": "androidx.collection:collection:1.4.4" + }, + { + "from": "androidx.compose.ui:ui-text-android:1.7.8", + "requested": "androidx.compose.runtime:runtime-saveable:1.7.8", + "selected": "androidx.compose.runtime:runtime-saveable:1.7.8" + }, + { + "from": "androidx.compose.ui:ui-text-android:1.7.8", + "requested": "androidx.compose.runtime:runtime:1.7.8", + "selected": "androidx.compose.runtime:runtime:1.7.8" + }, + { + "from": "androidx.compose.ui:ui-text-android:1.7.8", + "requested": "androidx.compose.ui:ui-geometry:1.7.8", + "selected": "androidx.compose.ui:ui-geometry:1.7.8" + }, + { + "from": "androidx.compose.ui:ui-text-android:1.7.8", + "requested": "androidx.compose.ui:ui-graphics:1.7.8", + "selected": "androidx.compose.ui:ui-graphics:1.7.8" + }, + { + "from": "androidx.compose.ui:ui-text-android:1.7.8", + "requested": "androidx.compose.ui:ui-tooling-preview:1.7.8", + "selected": "androidx.compose.ui:ui-tooling-preview:1.7.8" + }, + { + "from": "androidx.compose.ui:ui-text-android:1.7.8", + "requested": "androidx.compose.ui:ui-unit:1.7.8", + "selected": "androidx.compose.ui:ui-unit:1.7.8" + }, + { + "from": "androidx.compose.ui:ui-text-android:1.7.8", + "requested": "androidx.compose.ui:ui-util:1.7.8", + "selected": "androidx.compose.ui:ui-util:1.7.8" + }, + { + "from": "androidx.compose.ui:ui-text-android:1.7.8", + "requested": "androidx.compose.ui:ui:1.7.8", + "selected": "androidx.compose.ui:ui:1.7.8" + }, + { + "from": "androidx.compose.ui:ui-text-android:1.7.8", + "requested": "androidx.core:core:1.7.0", + "selected": "androidx.core:core:1.13.1" + }, + { + "from": "androidx.compose.ui:ui-text-android:1.7.8", + "requested": "androidx.emoji2:emoji2:1.2.0", + "selected": "androidx.emoji2:emoji2:1.3.0" + }, + { + "from": "androidx.compose.ui:ui-text-android:1.7.8", + "requested": "org.jetbrains.kotlin:kotlin-stdlib:1.8.22", + "selected": "org.jetbrains.kotlin:kotlin-stdlib:2.1.20" + }, + { + "from": "androidx.compose.ui:ui-text-android:1.7.8", + "requested": "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.7.3", + "selected": "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.7.3" + }, + { + "from": "androidx.compose.ui:ui-text:1.7.8", + "requested": "androidx.compose.ui:ui-text-android:1.7.8", + "selected": "androidx.compose.ui:ui-text-android:1.7.8" + }, + { + "from": "androidx.compose.ui:ui-tooling-preview-android:1.7.8", + "requested": "androidx.annotation:annotation:1.2.0", + "selected": "androidx.annotation:annotation:1.8.1" + }, + { + "from": "androidx.compose.ui:ui-tooling-preview-android:1.7.8", + "requested": "androidx.compose.runtime:runtime:1.7.8", + "selected": "androidx.compose.runtime:runtime:1.7.8" + }, + { + "from": "androidx.compose.ui:ui-tooling-preview-android:1.7.8", + "requested": "androidx.compose.ui:ui-geometry:1.7.8", + "selected": "androidx.compose.ui:ui-geometry:1.7.8" + }, + { + "from": "androidx.compose.ui:ui-tooling-preview-android:1.7.8", + "requested": "androidx.compose.ui:ui-graphics:1.7.8", + "selected": "androidx.compose.ui:ui-graphics:1.7.8" + }, + { + "from": "androidx.compose.ui:ui-tooling-preview-android:1.7.8", + "requested": "androidx.compose.ui:ui-text:1.7.8", + "selected": "androidx.compose.ui:ui-text:1.7.8" + }, + { + "from": "androidx.compose.ui:ui-tooling-preview-android:1.7.8", + "requested": "androidx.compose.ui:ui-unit:1.7.8", + "selected": "androidx.compose.ui:ui-unit:1.7.8" + }, + { + "from": "androidx.compose.ui:ui-tooling-preview-android:1.7.8", + "requested": "androidx.compose.ui:ui-util:1.7.8", + "selected": "androidx.compose.ui:ui-util:1.7.8" + }, + { + "from": "androidx.compose.ui:ui-tooling-preview-android:1.7.8", + "requested": "androidx.compose.ui:ui:1.7.8", + "selected": "androidx.compose.ui:ui:1.7.8" + }, + { + "from": "androidx.compose.ui:ui-tooling-preview-android:1.7.8", + "requested": "org.jetbrains.kotlin:kotlin-stdlib-common:1.8.22", + "selected": "org.jetbrains.kotlin:kotlin-stdlib-common:2.1.20" + }, + { + "from": "androidx.compose.ui:ui-tooling-preview:1.7.8", + "requested": "androidx.compose.ui:ui-tooling-preview-android:1.7.8", + "selected": "androidx.compose.ui:ui-tooling-preview-android:1.7.8" + }, + { + "from": "androidx.compose.ui:ui-unit-android:1.7.8", + "requested": "androidx.annotation:annotation-experimental:1.4.0", + "selected": "androidx.annotation:annotation-experimental:1.4.1" + }, + { + "from": "androidx.compose.ui:ui-unit-android:1.7.8", + "requested": "androidx.annotation:annotation:1.1.0", + "selected": "androidx.annotation:annotation:1.8.1" + }, + { + "from": "androidx.compose.ui:ui-unit-android:1.7.8", + "requested": "androidx.annotation:annotation:1.8.0", + "selected": "androidx.annotation:annotation:1.8.1" + }, + { + "from": "androidx.compose.ui:ui-unit-android:1.7.8", + "requested": "androidx.collection:collection-ktx:1.2.0", + "selected": "androidx.collection:collection-ktx:1.4.4" + }, + { + "from": "androidx.compose.ui:ui-unit-android:1.7.8", + "requested": "androidx.collection:collection:1.4.0", + "selected": "androidx.collection:collection:1.4.4" + }, + { + "from": "androidx.compose.ui:ui-unit-android:1.7.8", + "requested": "androidx.compose.runtime:runtime:1.7.8", + "selected": "androidx.compose.runtime:runtime:1.7.8" + }, + { + "from": "androidx.compose.ui:ui-unit-android:1.7.8", + "requested": "androidx.compose.ui:ui-geometry:1.7.8", + "selected": "androidx.compose.ui:ui-geometry:1.7.8" + }, + { + "from": "androidx.compose.ui:ui-unit-android:1.7.8", + "requested": "androidx.compose.ui:ui-graphics:1.7.8", + "selected": "androidx.compose.ui:ui-graphics:1.7.8" + }, + { + "from": "androidx.compose.ui:ui-unit-android:1.7.8", + "requested": "androidx.compose.ui:ui-text:1.7.8", + "selected": "androidx.compose.ui:ui-text:1.7.8" + }, + { + "from": "androidx.compose.ui:ui-unit-android:1.7.8", + "requested": "androidx.compose.ui:ui-tooling-preview:1.7.8", + "selected": "androidx.compose.ui:ui-tooling-preview:1.7.8" + }, + { + "from": "androidx.compose.ui:ui-unit-android:1.7.8", + "requested": "androidx.compose.ui:ui-util:1.7.8", + "selected": "androidx.compose.ui:ui-util:1.7.8" + }, + { + "from": "androidx.compose.ui:ui-unit-android:1.7.8", + "requested": "androidx.compose.ui:ui:1.7.8", + "selected": "androidx.compose.ui:ui:1.7.8" + }, + { + "from": "androidx.compose.ui:ui-unit-android:1.7.8", + "requested": "org.jetbrains.kotlin:kotlin-stdlib:1.8.22", + "selected": "org.jetbrains.kotlin:kotlin-stdlib:2.1.20" + }, + { + "from": "androidx.compose.ui:ui-unit:1.7.8", + "requested": "androidx.compose.ui:ui-unit-android:1.7.8", + "selected": "androidx.compose.ui:ui-unit-android:1.7.8" + }, + { + "from": "androidx.compose.ui:ui-util-android:1.7.8", + "requested": "androidx.annotation:annotation-experimental:1.4.0", + "selected": "androidx.annotation:annotation-experimental:1.4.1" + }, + { + "from": "androidx.compose.ui:ui-util-android:1.7.8", + "requested": "androidx.compose.ui:ui-geometry:1.7.8", + "selected": "androidx.compose.ui:ui-geometry:1.7.8" + }, + { + "from": "androidx.compose.ui:ui-util-android:1.7.8", + "requested": "androidx.compose.ui:ui-graphics:1.7.8", + "selected": "androidx.compose.ui:ui-graphics:1.7.8" + }, + { + "from": "androidx.compose.ui:ui-util-android:1.7.8", + "requested": "androidx.compose.ui:ui-text:1.7.8", + "selected": "androidx.compose.ui:ui-text:1.7.8" + }, + { + "from": "androidx.compose.ui:ui-util-android:1.7.8", + "requested": "androidx.compose.ui:ui-tooling-preview:1.7.8", + "selected": "androidx.compose.ui:ui-tooling-preview:1.7.8" + }, + { + "from": "androidx.compose.ui:ui-util-android:1.7.8", + "requested": "androidx.compose.ui:ui-unit:1.7.8", + "selected": "androidx.compose.ui:ui-unit:1.7.8" + }, + { + "from": "androidx.compose.ui:ui-util-android:1.7.8", + "requested": "androidx.compose.ui:ui:1.7.8", + "selected": "androidx.compose.ui:ui:1.7.8" + }, + { + "from": "androidx.compose.ui:ui-util-android:1.7.8", + "requested": "org.jetbrains.kotlin:kotlin-stdlib:1.8.22", + "selected": "org.jetbrains.kotlin:kotlin-stdlib:2.1.20" + }, + { + "from": "androidx.compose.ui:ui-util:1.7.8", + "requested": "androidx.compose.ui:ui-util-android:1.7.8", + "selected": "androidx.compose.ui:ui-util-android:1.7.8" + }, + { + "from": "androidx.compose.ui:ui:1.7.8", + "requested": "androidx.compose.ui:ui-android:1.7.8", + "selected": "androidx.compose.ui:ui-android:1.7.8" + }, + { + "from": "androidx.compose:compose-bom:2025.02.00", + "requested": "androidx.compose.animation:animation-android:1.7.8", + "selected": "androidx.compose.animation:animation-android:1.7.8" + }, + { + "from": "androidx.compose:compose-bom:2025.02.00", + "requested": "androidx.compose.animation:animation-core-android:1.7.8", + "selected": "androidx.compose.animation:animation-core-android:1.7.8" + }, + { + "from": "androidx.compose:compose-bom:2025.02.00", + "requested": "androidx.compose.animation:animation-core:1.7.8", + "selected": "androidx.compose.animation:animation-core:1.7.8" + }, + { + "from": "androidx.compose:compose-bom:2025.02.00", + "requested": "androidx.compose.animation:animation:1.7.8", + "selected": "androidx.compose.animation:animation:1.7.8" + }, + { + "from": "androidx.compose:compose-bom:2025.02.00", + "requested": "androidx.compose.foundation:foundation-android:1.7.8", + "selected": "androidx.compose.foundation:foundation-android:1.7.8" + }, + { + "from": "androidx.compose:compose-bom:2025.02.00", + "requested": "androidx.compose.foundation:foundation-layout-android:1.7.8", + "selected": "androidx.compose.foundation:foundation-layout-android:1.7.8" + }, + { + "from": "androidx.compose:compose-bom:2025.02.00", + "requested": "androidx.compose.foundation:foundation-layout:1.7.8", + "selected": "androidx.compose.foundation:foundation-layout:1.7.8" + }, + { + "from": "androidx.compose:compose-bom:2025.02.00", + "requested": "androidx.compose.foundation:foundation:1.7.8", + "selected": "androidx.compose.foundation:foundation:1.7.8" + }, + { + "from": "androidx.compose:compose-bom:2025.02.00", + "requested": "androidx.compose.material3:material3-android:1.3.1", + "selected": "androidx.compose.material3:material3-android:1.3.1" + }, + { + "from": "androidx.compose:compose-bom:2025.02.00", + "requested": "androidx.compose.material3:material3:1.3.1", + "selected": "androidx.compose.material3:material3:1.3.1" + }, + { + "from": "androidx.compose:compose-bom:2025.02.00", + "requested": "androidx.compose.material:material-icons-core-android:1.7.8", + "selected": "androidx.compose.material:material-icons-core-android:1.7.8" + }, + { + "from": "androidx.compose:compose-bom:2025.02.00", + "requested": "androidx.compose.material:material-icons-core:1.7.8", + "selected": "androidx.compose.material:material-icons-core:1.7.8" + }, + { + "from": "androidx.compose:compose-bom:2025.02.00", + "requested": "androidx.compose.material:material-ripple-android:1.7.8", + "selected": "androidx.compose.material:material-ripple-android:1.7.8" + }, + { + "from": "androidx.compose:compose-bom:2025.02.00", + "requested": "androidx.compose.material:material-ripple:1.7.8", + "selected": "androidx.compose.material:material-ripple:1.7.8" + }, + { + "from": "androidx.compose:compose-bom:2025.02.00", + "requested": "androidx.compose.runtime:runtime-android:1.7.8", + "selected": "androidx.compose.runtime:runtime-android:1.7.8" + }, + { + "from": "androidx.compose:compose-bom:2025.02.00", + "requested": "androidx.compose.runtime:runtime-saveable-android:1.7.8", + "selected": "androidx.compose.runtime:runtime-saveable-android:1.7.8" + }, + { + "from": "androidx.compose:compose-bom:2025.02.00", + "requested": "androidx.compose.runtime:runtime-saveable:1.7.8", + "selected": "androidx.compose.runtime:runtime-saveable:1.7.8" + }, + { + "from": "androidx.compose:compose-bom:2025.02.00", + "requested": "androidx.compose.runtime:runtime:1.7.8", + "selected": "androidx.compose.runtime:runtime:1.7.8" + }, + { + "from": "androidx.compose:compose-bom:2025.02.00", + "requested": "androidx.compose.ui:ui-android:1.7.8", + "selected": "androidx.compose.ui:ui-android:1.7.8" + }, + { + "from": "androidx.compose:compose-bom:2025.02.00", + "requested": "androidx.compose.ui:ui-geometry-android:1.7.8", + "selected": "androidx.compose.ui:ui-geometry-android:1.7.8" + }, + { + "from": "androidx.compose:compose-bom:2025.02.00", + "requested": "androidx.compose.ui:ui-geometry:1.7.8", + "selected": "androidx.compose.ui:ui-geometry:1.7.8" + }, + { + "from": "androidx.compose:compose-bom:2025.02.00", + "requested": "androidx.compose.ui:ui-graphics-android:1.7.8", + "selected": "androidx.compose.ui:ui-graphics-android:1.7.8" + }, + { + "from": "androidx.compose:compose-bom:2025.02.00", + "requested": "androidx.compose.ui:ui-graphics:1.7.8", + "selected": "androidx.compose.ui:ui-graphics:1.7.8" + }, + { + "from": "androidx.compose:compose-bom:2025.02.00", + "requested": "androidx.compose.ui:ui-text-android:1.7.8", + "selected": "androidx.compose.ui:ui-text-android:1.7.8" + }, + { + "from": "androidx.compose:compose-bom:2025.02.00", + "requested": "androidx.compose.ui:ui-text:1.7.8", + "selected": "androidx.compose.ui:ui-text:1.7.8" + }, + { + "from": "androidx.compose:compose-bom:2025.02.00", + "requested": "androidx.compose.ui:ui-tooling-preview-android:1.7.8", + "selected": "androidx.compose.ui:ui-tooling-preview-android:1.7.8" + }, + { + "from": "androidx.compose:compose-bom:2025.02.00", + "requested": "androidx.compose.ui:ui-tooling-preview:1.7.8", + "selected": "androidx.compose.ui:ui-tooling-preview:1.7.8" + }, + { + "from": "androidx.compose:compose-bom:2025.02.00", + "requested": "androidx.compose.ui:ui-unit-android:1.7.8", + "selected": "androidx.compose.ui:ui-unit-android:1.7.8" + }, + { + "from": "androidx.compose:compose-bom:2025.02.00", + "requested": "androidx.compose.ui:ui-unit:1.7.8", + "selected": "androidx.compose.ui:ui-unit:1.7.8" + }, + { + "from": "androidx.compose:compose-bom:2025.02.00", + "requested": "androidx.compose.ui:ui-util-android:1.7.8", + "selected": "androidx.compose.ui:ui-util-android:1.7.8" + }, + { + "from": "androidx.compose:compose-bom:2025.02.00", + "requested": "androidx.compose.ui:ui-util:1.7.8", + "selected": "androidx.compose.ui:ui-util:1.7.8" + }, + { + "from": "androidx.compose:compose-bom:2025.02.00", + "requested": "androidx.compose.ui:ui:1.7.8", + "selected": "androidx.compose.ui:ui:1.7.8" + }, + { + "from": "androidx.concurrent:concurrent-futures:1.1.0", + "requested": "androidx.annotation:annotation:1.1.0", + "selected": "androidx.annotation:annotation:1.8.1" + }, + { + "from": "androidx.concurrent:concurrent-futures:1.1.0", + "requested": "com.google.guava:listenablefuture:1.0", + "selected": "com.google.guava:listenablefuture:1.0" + }, + { + "from": "androidx.core:core-ktx:1.13.1", + "requested": "androidx.annotation:annotation:1.1.0", + "selected": "androidx.annotation:annotation:1.8.1" + }, + { + "from": "androidx.core:core-ktx:1.13.1", + "requested": "androidx.core:core:1.13.1", + "selected": "androidx.core:core:1.13.1" + }, + { + "from": "androidx.core:core-ktx:1.13.1", + "requested": "org.jetbrains.kotlin:kotlin-stdlib:1.8.22", + "selected": "org.jetbrains.kotlin:kotlin-stdlib:2.1.20" + }, + { + "from": "androidx.core:core:1.13.1", + "requested": "androidx.annotation:annotation-experimental:1.4.0", + "selected": "androidx.annotation:annotation-experimental:1.4.1" + }, + { + "from": "androidx.core:core:1.13.1", + "requested": "androidx.annotation:annotation:1.6.0", + "selected": "androidx.annotation:annotation:1.8.1" + }, + { + "from": "androidx.core:core:1.13.1", + "requested": "androidx.collection:collection:1.0.0", + "selected": "androidx.collection:collection:1.4.4" + }, + { + "from": "androidx.core:core:1.13.1", + "requested": "androidx.concurrent:concurrent-futures:1.0.0", + "selected": "androidx.concurrent:concurrent-futures:1.1.0" + }, + { + "from": "androidx.core:core:1.13.1", + "requested": "androidx.core:core-ktx:1.13.1", + "selected": "androidx.core:core-ktx:1.13.1" + }, + { + "from": "androidx.core:core:1.13.1", + "requested": "androidx.interpolator:interpolator:1.0.0", + "selected": "androidx.interpolator:interpolator:1.0.0" + }, + { + "from": "androidx.core:core:1.13.1", + "requested": "androidx.lifecycle:lifecycle-runtime:2.6.2", + "selected": "androidx.lifecycle:lifecycle-runtime:2.8.3" + }, + { + "from": "androidx.core:core:1.13.1", + "requested": "androidx.versionedparcelable:versionedparcelable:1.1.1", + "selected": "androidx.versionedparcelable:versionedparcelable:1.1.1" + }, + { + "from": "androidx.core:core:1.13.1", + "requested": "org.jetbrains.kotlin:kotlin-stdlib:1.8.22", + "selected": "org.jetbrains.kotlin:kotlin-stdlib:2.1.20" + }, + { + "from": "androidx.customview:customview-poolingcontainer:1.0.0", + "requested": "androidx.core:core-ktx:1.5.0", + "selected": "androidx.core:core-ktx:1.13.1" + }, + { + "from": "androidx.customview:customview-poolingcontainer:1.0.0", + "requested": "org.jetbrains.kotlin:kotlin-stdlib:1.6.21", + "selected": "org.jetbrains.kotlin:kotlin-stdlib:2.1.20" + }, + { + "from": "androidx.emoji2:emoji2:1.3.0", + "requested": "androidx.annotation:annotation:1.2.0", + "selected": "androidx.annotation:annotation:1.8.1" + }, + { + "from": "androidx.emoji2:emoji2:1.3.0", + "requested": "androidx.collection:collection:1.1.0", + "selected": "androidx.collection:collection:1.4.4" + }, + { + "from": "androidx.emoji2:emoji2:1.3.0", + "requested": "androidx.core:core:1.3.0", + "selected": "androidx.core:core:1.13.1" + }, + { + "from": "androidx.emoji2:emoji2:1.3.0", + "requested": "androidx.lifecycle:lifecycle-process:2.4.1", + "selected": "androidx.lifecycle:lifecycle-process:2.8.3" + }, + { + "from": "androidx.emoji2:emoji2:1.3.0", + "requested": "androidx.startup:startup-runtime:1.0.0", + "selected": "androidx.startup:startup-runtime:1.1.1" + }, + { + "from": "androidx.graphics:graphics-path:1.0.1", + "requested": "androidx.core:core:1.12.0", + "selected": "androidx.core:core:1.13.1" + }, + { + "from": "androidx.graphics:graphics-path:1.0.1", + "requested": "org.jetbrains.kotlin:kotlin-stdlib:1.8.22", + "selected": "org.jetbrains.kotlin:kotlin-stdlib:2.1.20" + }, + { + "from": "androidx.interpolator:interpolator:1.0.0", + "requested": "androidx.annotation:annotation:1.0.0", + "selected": "androidx.annotation:annotation:1.8.1" + }, + { + "from": "androidx.lifecycle:lifecycle-common-java8:2.8.3", + "requested": "androidx.annotation:annotation:1.1.0", + "selected": "androidx.annotation:annotation:1.8.1" + }, + { + "from": "androidx.lifecycle:lifecycle-common-java8:2.8.3", + "requested": "androidx.lifecycle:lifecycle-common:2.8.3", + "selected": "androidx.lifecycle:lifecycle-common:2.8.3" + }, + { + "from": "androidx.lifecycle:lifecycle-common-java8:2.8.3", + "requested": "androidx.lifecycle:lifecycle-livedata-core:2.8.3", + "selected": "androidx.lifecycle:lifecycle-livedata-core:2.8.3" + }, + { + "from": "androidx.lifecycle:lifecycle-common-java8:2.8.3", + "requested": "androidx.lifecycle:lifecycle-process:2.8.3", + "selected": "androidx.lifecycle:lifecycle-process:2.8.3" + }, + { + "from": "androidx.lifecycle:lifecycle-common-java8:2.8.3", + "requested": "androidx.lifecycle:lifecycle-runtime-compose:2.8.3", + "selected": "androidx.lifecycle:lifecycle-runtime-compose:2.8.3" + }, + { + "from": "androidx.lifecycle:lifecycle-common-java8:2.8.3", + "requested": "androidx.lifecycle:lifecycle-runtime-ktx:2.8.3", + "selected": "androidx.lifecycle:lifecycle-runtime-ktx:2.8.3" + }, + { + "from": "androidx.lifecycle:lifecycle-common-java8:2.8.3", + "requested": "androidx.lifecycle:lifecycle-runtime:2.8.3", + "selected": "androidx.lifecycle:lifecycle-runtime:2.8.3" + }, + { + "from": "androidx.lifecycle:lifecycle-common-java8:2.8.3", + "requested": "androidx.lifecycle:lifecycle-viewmodel-ktx:2.8.3", + "selected": "androidx.lifecycle:lifecycle-viewmodel-ktx:2.8.3" + }, + { + "from": "androidx.lifecycle:lifecycle-common-java8:2.8.3", + "requested": "androidx.lifecycle:lifecycle-viewmodel-savedstate:2.8.3", + "selected": "androidx.lifecycle:lifecycle-viewmodel-savedstate:2.8.3" + }, + { + "from": "androidx.lifecycle:lifecycle-common-java8:2.8.3", + "requested": "androidx.lifecycle:lifecycle-viewmodel:2.8.3", + "selected": "androidx.lifecycle:lifecycle-viewmodel:2.8.3" + }, + { + "from": "androidx.lifecycle:lifecycle-common-jvm:2.8.3", + "requested": "androidx.annotation:annotation:1.8.0", + "selected": "androidx.annotation:annotation:1.8.1" + }, + { + "from": "androidx.lifecycle:lifecycle-common-jvm:2.8.3", + "requested": "androidx.lifecycle:lifecycle-common-java8:2.8.3", + "selected": "androidx.lifecycle:lifecycle-common-java8:2.8.3" + }, + { + "from": "androidx.lifecycle:lifecycle-common-jvm:2.8.3", + "requested": "androidx.lifecycle:lifecycle-livedata-core:2.8.3", + "selected": "androidx.lifecycle:lifecycle-livedata-core:2.8.3" + }, + { + "from": "androidx.lifecycle:lifecycle-common-jvm:2.8.3", + "requested": "androidx.lifecycle:lifecycle-process:2.8.3", + "selected": "androidx.lifecycle:lifecycle-process:2.8.3" + }, + { + "from": "androidx.lifecycle:lifecycle-common-jvm:2.8.3", + "requested": "androidx.lifecycle:lifecycle-runtime-compose:2.8.3", + "selected": "androidx.lifecycle:lifecycle-runtime-compose:2.8.3" + }, + { + "from": "androidx.lifecycle:lifecycle-common-jvm:2.8.3", + "requested": "androidx.lifecycle:lifecycle-runtime-ktx:2.8.3", + "selected": "androidx.lifecycle:lifecycle-runtime-ktx:2.8.3" + }, + { + "from": "androidx.lifecycle:lifecycle-common-jvm:2.8.3", + "requested": "androidx.lifecycle:lifecycle-runtime:2.8.3", + "selected": "androidx.lifecycle:lifecycle-runtime:2.8.3" + }, + { + "from": "androidx.lifecycle:lifecycle-common-jvm:2.8.3", + "requested": "androidx.lifecycle:lifecycle-viewmodel-ktx:2.8.3", + "selected": "androidx.lifecycle:lifecycle-viewmodel-ktx:2.8.3" + }, + { + "from": "androidx.lifecycle:lifecycle-common-jvm:2.8.3", + "requested": "androidx.lifecycle:lifecycle-viewmodel-savedstate:2.8.3", + "selected": "androidx.lifecycle:lifecycle-viewmodel-savedstate:2.8.3" + }, + { + "from": "androidx.lifecycle:lifecycle-common-jvm:2.8.3", + "requested": "androidx.lifecycle:lifecycle-viewmodel:2.8.3", + "selected": "androidx.lifecycle:lifecycle-viewmodel:2.8.3" + }, + { + "from": "androidx.lifecycle:lifecycle-common-jvm:2.8.3", + "requested": "org.jetbrains.kotlin:kotlin-stdlib:1.8.22", + "selected": "org.jetbrains.kotlin:kotlin-stdlib:2.1.20" + }, + { + "from": "androidx.lifecycle:lifecycle-common-jvm:2.8.3", + "requested": "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.7.3", + "selected": "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.7.3" + }, + { + "from": "androidx.lifecycle:lifecycle-common:2.8.3", + "requested": "androidx.lifecycle:lifecycle-common-jvm:2.8.3", + "selected": "androidx.lifecycle:lifecycle-common-jvm:2.8.3" + }, + { + "from": "androidx.lifecycle:lifecycle-livedata-core:2.8.3", + "requested": "androidx.arch.core:core-common:2.2.0", + "selected": "androidx.arch.core:core-common:2.2.0" + }, + { + "from": "androidx.lifecycle:lifecycle-livedata-core:2.8.3", + "requested": "androidx.arch.core:core-runtime:2.2.0", + "selected": "androidx.arch.core:core-runtime:2.2.0" + }, + { + "from": "androidx.lifecycle:lifecycle-livedata-core:2.8.3", + "requested": "androidx.lifecycle:lifecycle-common-java8:2.8.3", + "selected": "androidx.lifecycle:lifecycle-common-java8:2.8.3" + }, + { + "from": "androidx.lifecycle:lifecycle-livedata-core:2.8.3", + "requested": "androidx.lifecycle:lifecycle-common:2.8.3", + "selected": "androidx.lifecycle:lifecycle-common:2.8.3" + }, + { + "from": "androidx.lifecycle:lifecycle-livedata-core:2.8.3", + "requested": "androidx.lifecycle:lifecycle-process:2.8.3", + "selected": "androidx.lifecycle:lifecycle-process:2.8.3" + }, + { + "from": "androidx.lifecycle:lifecycle-livedata-core:2.8.3", + "requested": "androidx.lifecycle:lifecycle-runtime-compose:2.8.3", + "selected": "androidx.lifecycle:lifecycle-runtime-compose:2.8.3" + }, + { + "from": "androidx.lifecycle:lifecycle-livedata-core:2.8.3", + "requested": "androidx.lifecycle:lifecycle-runtime-ktx:2.8.3", + "selected": "androidx.lifecycle:lifecycle-runtime-ktx:2.8.3" + }, + { + "from": "androidx.lifecycle:lifecycle-livedata-core:2.8.3", + "requested": "androidx.lifecycle:lifecycle-runtime:2.8.3", + "selected": "androidx.lifecycle:lifecycle-runtime:2.8.3" + }, + { + "from": "androidx.lifecycle:lifecycle-livedata-core:2.8.3", + "requested": "androidx.lifecycle:lifecycle-viewmodel-ktx:2.8.3", + "selected": "androidx.lifecycle:lifecycle-viewmodel-ktx:2.8.3" + }, + { + "from": "androidx.lifecycle:lifecycle-livedata-core:2.8.3", + "requested": "androidx.lifecycle:lifecycle-viewmodel-savedstate:2.8.3", + "selected": "androidx.lifecycle:lifecycle-viewmodel-savedstate:2.8.3" + }, + { + "from": "androidx.lifecycle:lifecycle-livedata-core:2.8.3", + "requested": "androidx.lifecycle:lifecycle-viewmodel:2.8.3", + "selected": "androidx.lifecycle:lifecycle-viewmodel:2.8.3" + }, + { + "from": "androidx.lifecycle:lifecycle-livedata-core:2.8.3", + "requested": "org.jetbrains.kotlin:kotlin-stdlib:1.8.22", + "selected": "org.jetbrains.kotlin:kotlin-stdlib:2.1.20" + }, + { + "from": "androidx.lifecycle:lifecycle-process:2.8.3", + "requested": "androidx.annotation:annotation:1.2.0", + "selected": "androidx.annotation:annotation:1.8.1" + }, + { + "from": "androidx.lifecycle:lifecycle-process:2.8.3", + "requested": "androidx.lifecycle:lifecycle-common-java8:2.8.3", + "selected": "androidx.lifecycle:lifecycle-common-java8:2.8.3" + }, + { + "from": "androidx.lifecycle:lifecycle-process:2.8.3", + "requested": "androidx.lifecycle:lifecycle-common:2.8.3", + "selected": "androidx.lifecycle:lifecycle-common:2.8.3" + }, + { + "from": "androidx.lifecycle:lifecycle-process:2.8.3", + "requested": "androidx.lifecycle:lifecycle-livedata-core:2.8.3", + "selected": "androidx.lifecycle:lifecycle-livedata-core:2.8.3" + }, + { + "from": "androidx.lifecycle:lifecycle-process:2.8.3", + "requested": "androidx.lifecycle:lifecycle-runtime-compose:2.8.3", + "selected": "androidx.lifecycle:lifecycle-runtime-compose:2.8.3" + }, + { + "from": "androidx.lifecycle:lifecycle-process:2.8.3", + "requested": "androidx.lifecycle:lifecycle-runtime-ktx:2.8.3", + "selected": "androidx.lifecycle:lifecycle-runtime-ktx:2.8.3" + }, + { + "from": "androidx.lifecycle:lifecycle-process:2.8.3", + "requested": "androidx.lifecycle:lifecycle-runtime:2.8.3", + "selected": "androidx.lifecycle:lifecycle-runtime:2.8.3" + }, + { + "from": "androidx.lifecycle:lifecycle-process:2.8.3", + "requested": "androidx.lifecycle:lifecycle-viewmodel-ktx:2.8.3", + "selected": "androidx.lifecycle:lifecycle-viewmodel-ktx:2.8.3" + }, + { + "from": "androidx.lifecycle:lifecycle-process:2.8.3", + "requested": "androidx.lifecycle:lifecycle-viewmodel-savedstate:2.8.3", + "selected": "androidx.lifecycle:lifecycle-viewmodel-savedstate:2.8.3" + }, + { + "from": "androidx.lifecycle:lifecycle-process:2.8.3", + "requested": "androidx.lifecycle:lifecycle-viewmodel:2.8.3", + "selected": "androidx.lifecycle:lifecycle-viewmodel:2.8.3" + }, + { + "from": "androidx.lifecycle:lifecycle-process:2.8.3", + "requested": "androidx.startup:startup-runtime:1.1.1", + "selected": "androidx.startup:startup-runtime:1.1.1" + }, + { + "from": "androidx.lifecycle:lifecycle-process:2.8.3", + "requested": "org.jetbrains.kotlin:kotlin-stdlib:1.8.22", + "selected": "org.jetbrains.kotlin:kotlin-stdlib:2.1.20" + }, + { + "from": "androidx.lifecycle:lifecycle-runtime-android:2.8.3", + "requested": "androidx.annotation:annotation:1.8.0", + "selected": "androidx.annotation:annotation:1.8.1" + }, + { + "from": "androidx.lifecycle:lifecycle-runtime-android:2.8.3", + "requested": "androidx.arch.core:core-common:2.2.0", + "selected": "androidx.arch.core:core-common:2.2.0" + }, + { + "from": "androidx.lifecycle:lifecycle-runtime-android:2.8.3", + "requested": "androidx.arch.core:core-runtime:2.2.0", + "selected": "androidx.arch.core:core-runtime:2.2.0" + }, + { + "from": "androidx.lifecycle:lifecycle-runtime-android:2.8.3", + "requested": "androidx.lifecycle:lifecycle-common-java8:2.8.3", + "selected": "androidx.lifecycle:lifecycle-common-java8:2.8.3" + }, + { + "from": "androidx.lifecycle:lifecycle-runtime-android:2.8.3", + "requested": "androidx.lifecycle:lifecycle-common:2.8.3", + "selected": "androidx.lifecycle:lifecycle-common:2.8.3" + }, + { + "from": "androidx.lifecycle:lifecycle-runtime-android:2.8.3", + "requested": "androidx.lifecycle:lifecycle-livedata-core:2.8.3", + "selected": "androidx.lifecycle:lifecycle-livedata-core:2.8.3" + }, + { + "from": "androidx.lifecycle:lifecycle-runtime-android:2.8.3", + "requested": "androidx.lifecycle:lifecycle-process:2.8.3", + "selected": "androidx.lifecycle:lifecycle-process:2.8.3" + }, + { + "from": "androidx.lifecycle:lifecycle-runtime-android:2.8.3", + "requested": "androidx.lifecycle:lifecycle-runtime-compose:2.8.3", + "selected": "androidx.lifecycle:lifecycle-runtime-compose:2.8.3" + }, + { + "from": "androidx.lifecycle:lifecycle-runtime-android:2.8.3", + "requested": "androidx.lifecycle:lifecycle-runtime-ktx:2.8.3", + "selected": "androidx.lifecycle:lifecycle-runtime-ktx:2.8.3" + }, + { + "from": "androidx.lifecycle:lifecycle-runtime-android:2.8.3", + "requested": "androidx.lifecycle:lifecycle-viewmodel-ktx:2.8.3", + "selected": "androidx.lifecycle:lifecycle-viewmodel-ktx:2.8.3" + }, + { + "from": "androidx.lifecycle:lifecycle-runtime-android:2.8.3", + "requested": "androidx.lifecycle:lifecycle-viewmodel-savedstate:2.8.3", + "selected": "androidx.lifecycle:lifecycle-viewmodel-savedstate:2.8.3" + }, + { + "from": "androidx.lifecycle:lifecycle-runtime-android:2.8.3", + "requested": "androidx.lifecycle:lifecycle-viewmodel:2.8.3", + "selected": "androidx.lifecycle:lifecycle-viewmodel:2.8.3" + }, + { + "from": "androidx.lifecycle:lifecycle-runtime-android:2.8.3", + "requested": "androidx.profileinstaller:profileinstaller:1.3.1", + "selected": "androidx.profileinstaller:profileinstaller:1.4.0" + }, + { + "from": "androidx.lifecycle:lifecycle-runtime-android:2.8.3", + "requested": "org.jetbrains.kotlin:kotlin-stdlib:1.8.22", + "selected": "org.jetbrains.kotlin:kotlin-stdlib:2.1.20" + }, + { + "from": "androidx.lifecycle:lifecycle-runtime-android:2.8.3", + "requested": "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.7.3", + "selected": "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.7.3" + }, + { + "from": "androidx.lifecycle:lifecycle-runtime-compose-android:2.8.3", + "requested": "androidx.annotation:annotation:1.8.0", + "selected": "androidx.annotation:annotation:1.8.1" + }, + { + "from": "androidx.lifecycle:lifecycle-runtime-compose-android:2.8.3", + "requested": "androidx.compose.runtime:runtime:1.6.5", + "selected": "androidx.compose.runtime:runtime:1.7.8" + }, + { + "from": "androidx.lifecycle:lifecycle-runtime-compose-android:2.8.3", + "requested": "androidx.lifecycle:lifecycle-common-java8:2.8.3", + "selected": "androidx.lifecycle:lifecycle-common-java8:2.8.3" + }, + { + "from": "androidx.lifecycle:lifecycle-runtime-compose-android:2.8.3", + "requested": "androidx.lifecycle:lifecycle-common:2.8.3", + "selected": "androidx.lifecycle:lifecycle-common:2.8.3" + }, + { + "from": "androidx.lifecycle:lifecycle-runtime-compose-android:2.8.3", + "requested": "androidx.lifecycle:lifecycle-livedata-core:2.8.3", + "selected": "androidx.lifecycle:lifecycle-livedata-core:2.8.3" + }, + { + "from": "androidx.lifecycle:lifecycle-runtime-compose-android:2.8.3", + "requested": "androidx.lifecycle:lifecycle-process:2.8.3", + "selected": "androidx.lifecycle:lifecycle-process:2.8.3" + }, + { + "from": "androidx.lifecycle:lifecycle-runtime-compose-android:2.8.3", + "requested": "androidx.lifecycle:lifecycle-runtime-ktx:2.8.3", + "selected": "androidx.lifecycle:lifecycle-runtime-ktx:2.8.3" + }, + { + "from": "androidx.lifecycle:lifecycle-runtime-compose-android:2.8.3", + "requested": "androidx.lifecycle:lifecycle-runtime:2.8.3", + "selected": "androidx.lifecycle:lifecycle-runtime:2.8.3" + }, + { + "from": "androidx.lifecycle:lifecycle-runtime-compose-android:2.8.3", + "requested": "androidx.lifecycle:lifecycle-viewmodel-ktx:2.8.3", + "selected": "androidx.lifecycle:lifecycle-viewmodel-ktx:2.8.3" + }, + { + "from": "androidx.lifecycle:lifecycle-runtime-compose-android:2.8.3", + "requested": "androidx.lifecycle:lifecycle-viewmodel-savedstate:2.8.3", + "selected": "androidx.lifecycle:lifecycle-viewmodel-savedstate:2.8.3" + }, + { + "from": "androidx.lifecycle:lifecycle-runtime-compose-android:2.8.3", + "requested": "androidx.lifecycle:lifecycle-viewmodel:2.8.3", + "selected": "androidx.lifecycle:lifecycle-viewmodel:2.8.3" + }, + { + "from": "androidx.lifecycle:lifecycle-runtime-compose:2.8.3", + "requested": "androidx.lifecycle:lifecycle-runtime-compose-android:2.8.3", + "selected": "androidx.lifecycle:lifecycle-runtime-compose-android:2.8.3" + }, + { + "from": "androidx.lifecycle:lifecycle-runtime-ktx-android:2.8.3", + "requested": "androidx.annotation:annotation:1.8.0", + "selected": "androidx.annotation:annotation:1.8.1" + }, + { + "from": "androidx.lifecycle:lifecycle-runtime-ktx-android:2.8.3", + "requested": "androidx.lifecycle:lifecycle-common-java8:2.8.3", + "selected": "androidx.lifecycle:lifecycle-common-java8:2.8.3" + }, + { + "from": "androidx.lifecycle:lifecycle-runtime-ktx-android:2.8.3", + "requested": "androidx.lifecycle:lifecycle-common:2.8.3", + "selected": "androidx.lifecycle:lifecycle-common:2.8.3" + }, + { + "from": "androidx.lifecycle:lifecycle-runtime-ktx-android:2.8.3", + "requested": "androidx.lifecycle:lifecycle-livedata-core:2.8.3", + "selected": "androidx.lifecycle:lifecycle-livedata-core:2.8.3" + }, + { + "from": "androidx.lifecycle:lifecycle-runtime-ktx-android:2.8.3", + "requested": "androidx.lifecycle:lifecycle-process:2.8.3", + "selected": "androidx.lifecycle:lifecycle-process:2.8.3" + }, + { + "from": "androidx.lifecycle:lifecycle-runtime-ktx-android:2.8.3", + "requested": "androidx.lifecycle:lifecycle-runtime-compose:2.8.3", + "selected": "androidx.lifecycle:lifecycle-runtime-compose:2.8.3" + }, + { + "from": "androidx.lifecycle:lifecycle-runtime-ktx-android:2.8.3", + "requested": "androidx.lifecycle:lifecycle-runtime:2.8.3", + "selected": "androidx.lifecycle:lifecycle-runtime:2.8.3" + }, + { + "from": "androidx.lifecycle:lifecycle-runtime-ktx-android:2.8.3", + "requested": "androidx.lifecycle:lifecycle-viewmodel-ktx:2.8.3", + "selected": "androidx.lifecycle:lifecycle-viewmodel-ktx:2.8.3" + }, + { + "from": "androidx.lifecycle:lifecycle-runtime-ktx-android:2.8.3", + "requested": "androidx.lifecycle:lifecycle-viewmodel-savedstate:2.8.3", + "selected": "androidx.lifecycle:lifecycle-viewmodel-savedstate:2.8.3" + }, + { + "from": "androidx.lifecycle:lifecycle-runtime-ktx-android:2.8.3", + "requested": "androidx.lifecycle:lifecycle-viewmodel:2.8.3", + "selected": "androidx.lifecycle:lifecycle-viewmodel:2.8.3" + }, + { + "from": "androidx.lifecycle:lifecycle-runtime-ktx-android:2.8.3", + "requested": "org.jetbrains.kotlin:kotlin-stdlib:1.8.22", + "selected": "org.jetbrains.kotlin:kotlin-stdlib:2.1.20" + }, + { + "from": "androidx.lifecycle:lifecycle-runtime-ktx-android:2.8.3", + "requested": "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.7.3", + "selected": "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.7.3" + }, + { + "from": "androidx.lifecycle:lifecycle-runtime-ktx:2.8.3", + "requested": "androidx.lifecycle:lifecycle-runtime-ktx-android:2.8.3", + "selected": "androidx.lifecycle:lifecycle-runtime-ktx-android:2.8.3" + }, + { + "from": "androidx.lifecycle:lifecycle-runtime:2.8.3", + "requested": "androidx.lifecycle:lifecycle-runtime-android:2.8.3", + "selected": "androidx.lifecycle:lifecycle-runtime-android:2.8.3" + }, + { + "from": "androidx.lifecycle:lifecycle-viewmodel-android:2.8.3", + "requested": "androidx.annotation:annotation:1.8.0", + "selected": "androidx.annotation:annotation:1.8.1" + }, + { + "from": "androidx.lifecycle:lifecycle-viewmodel-android:2.8.3", + "requested": "androidx.lifecycle:lifecycle-common-java8:2.8.3", + "selected": "androidx.lifecycle:lifecycle-common-java8:2.8.3" + }, + { + "from": "androidx.lifecycle:lifecycle-viewmodel-android:2.8.3", + "requested": "androidx.lifecycle:lifecycle-common:2.8.3", + "selected": "androidx.lifecycle:lifecycle-common:2.8.3" + }, + { + "from": "androidx.lifecycle:lifecycle-viewmodel-android:2.8.3", + "requested": "androidx.lifecycle:lifecycle-livedata-core:2.8.3", + "selected": "androidx.lifecycle:lifecycle-livedata-core:2.8.3" + }, + { + "from": "androidx.lifecycle:lifecycle-viewmodel-android:2.8.3", + "requested": "androidx.lifecycle:lifecycle-process:2.8.3", + "selected": "androidx.lifecycle:lifecycle-process:2.8.3" + }, + { + "from": "androidx.lifecycle:lifecycle-viewmodel-android:2.8.3", + "requested": "androidx.lifecycle:lifecycle-runtime-compose:2.8.3", + "selected": "androidx.lifecycle:lifecycle-runtime-compose:2.8.3" + }, + { + "from": "androidx.lifecycle:lifecycle-viewmodel-android:2.8.3", + "requested": "androidx.lifecycle:lifecycle-runtime-ktx:2.8.3", + "selected": "androidx.lifecycle:lifecycle-runtime-ktx:2.8.3" + }, + { + "from": "androidx.lifecycle:lifecycle-viewmodel-android:2.8.3", + "requested": "androidx.lifecycle:lifecycle-runtime:2.8.3", + "selected": "androidx.lifecycle:lifecycle-runtime:2.8.3" + }, + { + "from": "androidx.lifecycle:lifecycle-viewmodel-android:2.8.3", + "requested": "androidx.lifecycle:lifecycle-viewmodel-ktx:2.8.3", + "selected": "androidx.lifecycle:lifecycle-viewmodel-ktx:2.8.3" + }, + { + "from": "androidx.lifecycle:lifecycle-viewmodel-android:2.8.3", + "requested": "androidx.lifecycle:lifecycle-viewmodel-savedstate:2.8.3", + "selected": "androidx.lifecycle:lifecycle-viewmodel-savedstate:2.8.3" + }, + { + "from": "androidx.lifecycle:lifecycle-viewmodel-android:2.8.3", + "requested": "org.jetbrains.kotlin:kotlin-stdlib:1.8.22", + "selected": "org.jetbrains.kotlin:kotlin-stdlib:2.1.20" + }, + { + "from": "androidx.lifecycle:lifecycle-viewmodel-android:2.8.3", + "requested": "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.7.3", + "selected": "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.7.3" + }, + { + "from": "androidx.lifecycle:lifecycle-viewmodel-android:2.8.3", + "requested": "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.7.3", + "selected": "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.7.3" + }, + { + "from": "androidx.lifecycle:lifecycle-viewmodel-ktx:2.8.3", + "requested": "androidx.lifecycle:lifecycle-common-java8:2.8.3", + "selected": "androidx.lifecycle:lifecycle-common-java8:2.8.3" + }, + { + "from": "androidx.lifecycle:lifecycle-viewmodel-ktx:2.8.3", + "requested": "androidx.lifecycle:lifecycle-common:2.8.3", + "selected": "androidx.lifecycle:lifecycle-common:2.8.3" + }, + { + "from": "androidx.lifecycle:lifecycle-viewmodel-ktx:2.8.3", + "requested": "androidx.lifecycle:lifecycle-livedata-core:2.8.3", + "selected": "androidx.lifecycle:lifecycle-livedata-core:2.8.3" + }, + { + "from": "androidx.lifecycle:lifecycle-viewmodel-ktx:2.8.3", + "requested": "androidx.lifecycle:lifecycle-process:2.8.3", + "selected": "androidx.lifecycle:lifecycle-process:2.8.3" + }, + { + "from": "androidx.lifecycle:lifecycle-viewmodel-ktx:2.8.3", + "requested": "androidx.lifecycle:lifecycle-runtime-compose:2.8.3", + "selected": "androidx.lifecycle:lifecycle-runtime-compose:2.8.3" + }, + { + "from": "androidx.lifecycle:lifecycle-viewmodel-ktx:2.8.3", + "requested": "androidx.lifecycle:lifecycle-runtime-ktx:2.8.3", + "selected": "androidx.lifecycle:lifecycle-runtime-ktx:2.8.3" + }, + { + "from": "androidx.lifecycle:lifecycle-viewmodel-ktx:2.8.3", + "requested": "androidx.lifecycle:lifecycle-runtime:2.8.3", + "selected": "androidx.lifecycle:lifecycle-runtime:2.8.3" + }, + { + "from": "androidx.lifecycle:lifecycle-viewmodel-ktx:2.8.3", + "requested": "androidx.lifecycle:lifecycle-viewmodel-savedstate:2.8.3", + "selected": "androidx.lifecycle:lifecycle-viewmodel-savedstate:2.8.3" + }, + { + "from": "androidx.lifecycle:lifecycle-viewmodel-ktx:2.8.3", + "requested": "androidx.lifecycle:lifecycle-viewmodel:2.8.3", + "selected": "androidx.lifecycle:lifecycle-viewmodel:2.8.3" + }, + { + "from": "androidx.lifecycle:lifecycle-viewmodel-ktx:2.8.3", + "requested": "org.jetbrains.kotlin:kotlin-stdlib:1.8.22", + "selected": "org.jetbrains.kotlin:kotlin-stdlib:2.1.20" + }, + { + "from": "androidx.lifecycle:lifecycle-viewmodel-ktx:2.8.3", + "requested": "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.7.3", + "selected": "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.7.3" + }, + { + "from": "androidx.lifecycle:lifecycle-viewmodel-savedstate:2.8.3", + "requested": "androidx.annotation:annotation:1.0.0", + "selected": "androidx.annotation:annotation:1.8.1" + }, + { + "from": "androidx.lifecycle:lifecycle-viewmodel-savedstate:2.8.3", + "requested": "androidx.core:core-ktx:1.2.0", + "selected": "androidx.core:core-ktx:1.13.1" + }, + { + "from": "androidx.lifecycle:lifecycle-viewmodel-savedstate:2.8.3", + "requested": "androidx.lifecycle:lifecycle-common-java8:2.8.3", + "selected": "androidx.lifecycle:lifecycle-common-java8:2.8.3" + }, + { + "from": "androidx.lifecycle:lifecycle-viewmodel-savedstate:2.8.3", + "requested": "androidx.lifecycle:lifecycle-common:2.8.3", + "selected": "androidx.lifecycle:lifecycle-common:2.8.3" + }, + { + "from": "androidx.lifecycle:lifecycle-viewmodel-savedstate:2.8.3", + "requested": "androidx.lifecycle:lifecycle-livedata-core:2.8.3", + "selected": "androidx.lifecycle:lifecycle-livedata-core:2.8.3" + }, + { + "from": "androidx.lifecycle:lifecycle-viewmodel-savedstate:2.8.3", + "requested": "androidx.lifecycle:lifecycle-process:2.8.3", + "selected": "androidx.lifecycle:lifecycle-process:2.8.3" + }, + { + "from": "androidx.lifecycle:lifecycle-viewmodel-savedstate:2.8.3", + "requested": "androidx.lifecycle:lifecycle-runtime-compose:2.8.3", + "selected": "androidx.lifecycle:lifecycle-runtime-compose:2.8.3" + }, + { + "from": "androidx.lifecycle:lifecycle-viewmodel-savedstate:2.8.3", + "requested": "androidx.lifecycle:lifecycle-runtime-ktx:2.8.3", + "selected": "androidx.lifecycle:lifecycle-runtime-ktx:2.8.3" + }, + { + "from": "androidx.lifecycle:lifecycle-viewmodel-savedstate:2.8.3", + "requested": "androidx.lifecycle:lifecycle-runtime:2.8.3", + "selected": "androidx.lifecycle:lifecycle-runtime:2.8.3" + }, + { + "from": "androidx.lifecycle:lifecycle-viewmodel-savedstate:2.8.3", + "requested": "androidx.lifecycle:lifecycle-viewmodel-ktx:2.8.3", + "selected": "androidx.lifecycle:lifecycle-viewmodel-ktx:2.8.3" + }, + { + "from": "androidx.lifecycle:lifecycle-viewmodel-savedstate:2.8.3", + "requested": "androidx.lifecycle:lifecycle-viewmodel:2.8.3", + "selected": "androidx.lifecycle:lifecycle-viewmodel:2.8.3" + }, + { + "from": "androidx.lifecycle:lifecycle-viewmodel-savedstate:2.8.3", + "requested": "androidx.savedstate:savedstate:1.2.1", + "selected": "androidx.savedstate:savedstate:1.2.1" + }, + { + "from": "androidx.lifecycle:lifecycle-viewmodel-savedstate:2.8.3", + "requested": "org.jetbrains.kotlin:kotlin-stdlib:1.8.22", + "selected": "org.jetbrains.kotlin:kotlin-stdlib:2.1.20" + }, + { + "from": "androidx.lifecycle:lifecycle-viewmodel-savedstate:2.8.3", + "requested": "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.7.3", + "selected": "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.7.3" + }, + { + "from": "androidx.lifecycle:lifecycle-viewmodel:2.8.3", + "requested": "androidx.lifecycle:lifecycle-viewmodel-android:2.8.3", + "selected": "androidx.lifecycle:lifecycle-viewmodel-android:2.8.3" + }, + { + "from": "androidx.profileinstaller:profileinstaller:1.4.0", + "requested": "androidx.annotation:annotation:1.8.1", + "selected": "androidx.annotation:annotation:1.8.1" + }, + { + "from": "androidx.profileinstaller:profileinstaller:1.4.0", + "requested": "androidx.concurrent:concurrent-futures:1.1.0", + "selected": "androidx.concurrent:concurrent-futures:1.1.0" + }, + { + "from": "androidx.profileinstaller:profileinstaller:1.4.0", + "requested": "androidx.startup:startup-runtime:1.1.1", + "selected": "androidx.startup:startup-runtime:1.1.1" + }, + { + "from": "androidx.profileinstaller:profileinstaller:1.4.0", + "requested": "com.google.guava:listenablefuture:1.0", + "selected": "com.google.guava:listenablefuture:1.0" + }, + { + "from": "androidx.savedstate:savedstate-ktx:1.2.1", + "requested": "androidx.savedstate:savedstate:1.2.1", + "selected": "androidx.savedstate:savedstate:1.2.1" + }, + { + "from": "androidx.savedstate:savedstate-ktx:1.2.1", + "requested": "org.jetbrains.kotlin:kotlin-stdlib:1.8.10", + "selected": "org.jetbrains.kotlin:kotlin-stdlib:2.1.20" + }, + { + "from": "androidx.savedstate:savedstate:1.2.1", + "requested": "androidx.annotation:annotation:1.1.0", + "selected": "androidx.annotation:annotation:1.8.1" + }, + { + "from": "androidx.savedstate:savedstate:1.2.1", + "requested": "androidx.arch.core:core-common:2.1.0", + "selected": "androidx.arch.core:core-common:2.2.0" + }, + { + "from": "androidx.savedstate:savedstate:1.2.1", + "requested": "androidx.lifecycle:lifecycle-common:2.6.1", + "selected": "androidx.lifecycle:lifecycle-common:2.8.3" + }, + { + "from": "androidx.savedstate:savedstate:1.2.1", + "requested": "androidx.savedstate:savedstate-ktx:1.2.1", + "selected": "androidx.savedstate:savedstate-ktx:1.2.1" + }, + { + "from": "androidx.savedstate:savedstate:1.2.1", + "requested": "org.jetbrains.kotlin:kotlin-stdlib:1.8.10", + "selected": "org.jetbrains.kotlin:kotlin-stdlib:2.1.20" + }, + { + "from": "androidx.startup:startup-runtime:1.1.1", + "requested": "androidx.annotation:annotation:1.1.0", + "selected": "androidx.annotation:annotation:1.8.1" + }, + { + "from": "androidx.startup:startup-runtime:1.1.1", + "requested": "androidx.tracing:tracing:1.0.0", + "selected": "androidx.tracing:tracing:1.0.0" + }, + { + "from": "androidx.tracing:tracing:1.0.0", + "requested": "androidx.annotation:annotation:1.1.0", + "selected": "androidx.annotation:annotation:1.8.1" + }, + { + "from": "androidx.versionedparcelable:versionedparcelable:1.1.1", + "requested": "androidx.annotation:annotation:1.1.0", + "selected": "androidx.annotation:annotation:1.8.1" + }, + { + "from": "androidx.versionedparcelable:versionedparcelable:1.1.1", + "requested": "androidx.collection:collection:1.0.0", + "selected": "androidx.collection:collection:1.4.4" + }, + { + "from": "org.jetbrains.kotlin:kotlin-stdlib-common:2.1.20", + "requested": "org.jetbrains.kotlin:kotlin-stdlib:2.1.20", + "selected": "org.jetbrains.kotlin:kotlin-stdlib:2.1.20" + }, + { + "from": "org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.8.20", + "requested": "org.jetbrains.kotlin:kotlin-stdlib:1.8.20", + "selected": "org.jetbrains.kotlin:kotlin-stdlib:2.1.20" + }, + { + "from": "org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.8.20", + "requested": "org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.8.20", + "selected": "org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.8.20" + }, + { + "from": "org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.8.20", + "requested": "org.jetbrains.kotlin:kotlin-stdlib:1.8.20", + "selected": "org.jetbrains.kotlin:kotlin-stdlib:2.1.20" + }, + { + "from": "org.jetbrains.kotlin:kotlin-stdlib:2.1.20", + "requested": "org.jetbrains.kotlin:kotlin-stdlib-common:2.1.20", + "selected": "org.jetbrains.kotlin:kotlin-stdlib-common:2.1.20" + }, + { + "from": "org.jetbrains.kotlin:kotlin-stdlib:2.1.20", + "requested": "org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.8.0", + "selected": "org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.8.20" + }, + { + "from": "org.jetbrains.kotlin:kotlin-stdlib:2.1.20", + "requested": "org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.8.0", + "selected": "org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.8.20" + }, + { + "from": "org.jetbrains.kotlin:kotlin-stdlib:2.1.20", + "requested": "org.jetbrains:annotations:13.0", + "selected": "org.jetbrains:annotations:23.0.0" + }, + { + "from": "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.7.3", + "requested": "org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.8.20", + "selected": "org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.8.20" + }, + { + "from": "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.7.3", + "requested": "org.jetbrains.kotlinx:kotlinx-coroutines-bom:1.7.3", + "selected": "org.jetbrains.kotlinx:kotlinx-coroutines-bom:1.7.3" + }, + { + "from": "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.7.3", + "requested": "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.7.3", + "selected": "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.7.3" + }, + { + "from": "org.jetbrains.kotlinx:kotlinx-coroutines-bom:1.7.3", + "requested": "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.7.3", + "selected": "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.7.3" + }, + { + "from": "org.jetbrains.kotlinx:kotlinx-coroutines-bom:1.7.3", + "requested": "org.jetbrains.kotlinx:kotlinx-coroutines-core-jvm:1.7.3", + "selected": "org.jetbrains.kotlinx:kotlinx-coroutines-core-jvm:1.7.3" + }, + { + "from": "org.jetbrains.kotlinx:kotlinx-coroutines-bom:1.7.3", + "requested": "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.7.3", + "selected": "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.7.3" + }, + { + "from": "org.jetbrains.kotlinx:kotlinx-coroutines-core-jvm:1.7.3", + "requested": "org.jetbrains.kotlin:kotlin-stdlib-common:1.8.20", + "selected": "org.jetbrains.kotlin:kotlin-stdlib-common:2.1.20" + }, + { + "from": "org.jetbrains.kotlinx:kotlinx-coroutines-core-jvm:1.7.3", + "requested": "org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.8.20", + "selected": "org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.8.20" + }, + { + "from": "org.jetbrains.kotlinx:kotlinx-coroutines-core-jvm:1.7.3", + "requested": "org.jetbrains.kotlinx:kotlinx-coroutines-bom:1.7.3", + "selected": "org.jetbrains.kotlinx:kotlinx-coroutines-bom:1.7.3" + }, + { + "from": "org.jetbrains.kotlinx:kotlinx-coroutines-core-jvm:1.7.3", + "requested": "org.jetbrains:annotations:23.0.0", + "selected": "org.jetbrains:annotations:23.0.0" + }, + { + "from": "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.7.3", + "requested": "org.jetbrains.kotlinx:kotlinx-coroutines-core-jvm:1.7.3", + "selected": "org.jetbrains.kotlinx:kotlinx-coroutines-core-jvm:1.7.3" + }, + { + "from": "project :app", + "requested": "androidx.activity:activity-compose:1.10.0", + "selected": "androidx.activity:activity-compose:1.10.0" + }, + { + "from": "project :app", + "requested": "androidx.compose.animation:animation-core", + "selected": "androidx.compose.animation:animation-core:1.7.8" + }, + { + "from": "project :app", + "requested": "androidx.compose.foundation:foundation", + "selected": "androidx.compose.foundation:foundation:1.7.8" + }, + { + "from": "project :app", + "requested": "androidx.compose.material3:material3", + "selected": "androidx.compose.material3:material3:1.3.1" + }, + { + "from": "project :app", + "requested": "androidx.compose.ui:ui", + "selected": "androidx.compose.ui:ui:1.7.8" + }, + { + "from": "project :app", + "requested": "androidx.compose.ui:ui-tooling-preview", + "selected": "androidx.compose.ui:ui-tooling-preview:1.7.8" + }, + { + "from": "project :app", + "requested": "androidx.compose:compose-bom:2025.02.00", + "selected": "androidx.compose:compose-bom:2025.02.00" + }, + { + "from": "project :app", + "requested": "org.jetbrains.kotlin:kotlin-stdlib:2.1.20", + "selected": "org.jetbrains.kotlin:kotlin-stdlib:2.1.20" + } + ], + "counts": { + "declared_dependencies": 7, + "resolved_components": 83, + "dependency_edges": 457 + } + }, + "ios": { + "target": "Nightingale", + "third_party_runtime_packages": [], + "apple_system_modules": [ + "Combine", + "Foundation", + "Security", + "SwiftUI", + "UIKit" + ], + "package_manager_inputs_present": false, + "counts": { + "third_party_runtime_packages": 0, + "apple_system_modules": 5 + } + }, + "interpretation": [ + "This file is an exact generated inventory of the bounded foundation inputs described in scope.", + "An empty iOS third-party package list means no third-party package manager is declared in the current XcodeGen source; it does not inventory Apple SDK or operating-system components.", + "Android entries are resolution facts for the named Release runtime configuration, not approval, license, provenance, or vulnerability findings.", + "Any source-hash or resolved-graph change requires regeneration, review, and a fresh exact-SHA CI result.", + "This inventory does not authorize patient data, networking, identity, disclosure, messaging, production access, distribution, or clinical use." + ] +} diff --git a/docs/plans/nightingale-patient-product-2026-07-26.md b/docs/plans/nightingale-patient-product-2026-07-26.md new file mode 100644 index 00000000..78041f7e --- /dev/null +++ b/docs/plans/nightingale-patient-product-2026-07-26.md @@ -0,0 +1,712 @@ +# Nightingale Patient Product — Direction, Migration, and Implementation Plan + +**Status:** Active product-direction plan; 42/54 bounded items complete (77.78%); no +patient feature is activated or pilot-ready. + +**Companion execution log:** +[DEVLOG-nightingale-patient-product-2026-07-26.md](../devlog/DEVLOG-nightingale-patient-product-2026-07-26.md) + +**Supersedes for new patient-product work:** the patient-facing portions of +[the Hummingbird parity and patient-experience plan](../hummingbird/ZEPHYRUS-HUMMINGBIRD-FUNCTIONAL-PARITY-AND-PATIENT-EXPERIENCE-PLAN-2026-07-19.md). +It does **not** supersede Hummingbird Staff parity work, the existing patient safety +evidence, or any clinical/governance decision. + +## 1. Decision and product boundary + +Zephyrus will have two independent mobile products: + +| Product | Audience and purpose | Native identities | Explicitly excluded | +| --------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------- | +| **Hummingbird** | Hospital staff: operational awareness and accountable action during a shift | iOS `net.acumenus.hummingbird`; Android `net.acumenus.hummingbird` | Patient enrollment, patient credentials, patient-facing pathway views, and patient messaging UI | +| **Nightingale** | Inpatients and permitted representatives: understand released care information, prepare questions, and communicate through approved care-team workflows | iOS `net.acumenus.nightingale`; Android `net.acumenus.nightingale` | Staff operational boards, staff identities/tokens, raw clinical source data, order entry, diagnosis/triage, and autonomous clinical recommendations | + +`Hummingbird Patient` is therefore **not** a branded production product. Its current +native targets, patient BFF contract, safety controls, and tests are retained as an +auditable implementation reference. They must not be renamed in place, re-issued under a +new bundle ID, or activated by configuration. Nightingale is a fresh product stream with +its own release identity, support model, signing posture, app-store records, analytics and +crash boundaries, privacy review, and pilot authorization. + +This decision strengthens the already-accepted patient-product architectural boundary: +separate patient artifacts are required, but now they also carry a distinct patient product +name, brand, ownership, and release train. + +## 2. What changes now + +### 2.1 Product naming + +- **Hummingbird** means the staff mobile companion only. Staff routes remain under + `/api/mobile/v1`; staff identifiers, notifications, widgets, and operational roles stay + exclusive to Hummingbird. +- **Nightingale** means the patient mobile product only. The existing + `/api/patient/v1` boundary remains a backend _compatibility boundary_ until a separately + reviewed Nightingale contract/version migration is complete; it is not a license to + ship the old Hummingbird Patient binary. +- User-facing copy, App Store metadata, support guides, notification copy, deep links, + app groups, signing, keychain aliases, analytics/crash projects, screenshots, and + accessibility labels must use the correct product name. No binary may display both + brands as if they are alternate modes of one application. + +### 2.2 Brand assets supplied for this direction + +| Asset | Product use | Source fingerprint | Processing rule | +| ----------------- | ---------------------------- | -------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------ | +| `Hummingbird.png` | Hummingbird staff app icon | SHA-256 `5ecc70c2a85d9d6471aabb76cbc49b42a976f6b66ba22c84af065a625fe6e8ad` | Create platform-specific, opaque icon derivatives; preserve the supplied source outside release binaries for provenance. | +| `Nightingale.png` | Nightingale patient app icon | SHA-256 `e97191b7d1eccc32c6a1aa95f0ba2329e1cfb4c1ac1c9b3d2d540872b3327c76` | Create platform-specific, opaque icon derivatives only in the new Nightingale targets. | + +Both supplied PNGs are 1254 × 1254 RGBA source assets. App-icon derivatives must be +rendered against a reviewed opaque background: iOS icons must not contain transparency, +and Android adaptive icons need a safe-zone foreground plus an opaque background and +monochrome fallback. This is branding implementation, not a clinical-content approval or +an assertion of intellectual-property clearance; ownership/license provenance must be +recorded before App Store or pilot distribution. + +## 3. Current-state reconciliation + +| Current item | Disposition | Why | +| ------------------------------------------------------------------------- | --------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | +| `hummingbird/iosApp`, `hummingbird/androidApp` | Continue as Hummingbird Staff | Already use staff-only app IDs and staff mobile BFF. Their icon will be replaced with the supplied Hummingbird mark without changing identity, data scope, or release policy. | +| `hummingbird/iosPatientApp`, `hummingbird/androidPatientApp` | Freeze as a non-production reference | They contain useful patient projection, privacy-cover, secure-storage, messaging, accessibility, and emulator evidence, but their app names/IDs still bind them to Hummingbird. No new feature or production activation work lands there after the Nightingale scaffold is accepted. | +| `docs/hummingbird/api-contract/hummingbird-patient.v1.yaml` | Treat as a governed compatibility input | Preserve its route and test evidence. Do not silently rename, widen, or activate it. Build a Nightingale contract/ownership surface before its first product release. | +| Patient identity, grants, projections, messaging kernel, release policies | Reuse only through explicit Nightingale adapters | No patient data model or raw source is copied to a client. Existing patient controls are candidate implementation inputs, not proof of Nightingale clinical approval. | +| Existing Hummingbird plan/checklist | Keep as historic evidence; redirect new patient rows here | Never rewrite past evidence to imply Nightingale completion. Each completed Nightingale control receives its own dated evidence and checklist entry. | + +## 4. Nightingale product charter + +Nightingale helps an inpatient answer four patient-centered questions without making +clinical decisions on the patient's behalf: + +1. **What is happening now?** — a released, plain-language Today view with freshness, + uncertainty, correction/retraction, and urgent-help boundaries. +2. **What may happen next?** — a clinician-approved and release-governed pathway, + discharge preparation, education, and questions to ask; never a diagnosis, promise, + order, or automated care plan. +3. **Who can help me?** — an approved Care Team view that exposes only released roles and + allowed contact routes, never staff schedules, direct personal data, or internal notes. +4. **How can I communicate safely?** — a capability- and encounter-scoped conversation + workflow with clear non-emergency guidance, accountable routing, handoff, escalation, + retention, correction, and accurate delivery state. + +The product is not a general portal, a replacement for the bedside call system, a staff +workstation, a Home Hospital app, or an AI clinician. Nightingale Eddy, if ever explored, +is a separate non-actioning explanation study and remains disabled unless its own safety +case is approved. + +## 5. Non-negotiable architecture + +### 5.1 Native application isolation + +Create independent source roots and targets: + +```text +nightingale/ + iosApp/ # target, tests, assets, entitlements, project generator + androidApp/ # one Android application module and tests + brand/ # source/provenance only; not referenced at runtime +docs/nightingale/ + README.md # product/contract and evidence index + api-contract/ # Nightingale-owned contract and fixtures + safety/ # approved matrices and release evidence +``` + +The future iOS target uses `net.acumenus.nightingale`; Android uses +`net.acumenus.nightingale`. Each has its own signing IDs, keychain service or encrypted +storage namespace, Universal/App Links, notification channel/topic, deep-link host/path +allowlist, analytics/crash project, privacy manifest, release artifact, and support +telemetry. Neither source tree may import Hummingbird Staff packages or make staff API +calls. Shared code is limited to reviewed, non-PHI build tooling, design-token generation, +and generated contract artifacts where the generator proves no staff operation enters the +output. + +### 5.2 Backend and contract isolation + +- Keep patient authorization fail-closed by principal, effective relationship, active + encounter/grant, scope, facility/unit policy, feature gate, content release, and + disclosure policy. +- Maintain patient APIs as a separate contract namespace and independently version its + Nightingale ownership. A route alias, client migration, or endpoint rename requires an + ADR, compatibility fixture suite, deprecation window, audit verification, and rollback. +- Read only released patient projections. Raw EHR/FHIR material, staff notes, operational + scores, inferred risks, draft/review content, source identifiers, and unreleased pathway + state must be impossible to serialize into Nightingale envelopes. +- Patient writes remain deterministic, explicit, idempotent where specified, auditable, + and never queued into an opaque offline outbox. A network ambiguity must not claim that + a message, acknowledgement, consent, or goal change succeeded. + +### 5.3 Safety, privacy, and content isolation + +- Application development does not create production fixtures, sample principals, active + grants, releases, sessions, or feature flags. The sole exception is the separately and + explicitly authorized 2026-07-27 production sample clone: one synthetic operational + encounter and one pending/inactive, contactless/passwordless Nightingale principal, + documented under `docs/evidence/nightingale/production-sample-patient-2026-07-27/`. + It has no identity link, grant, challenge, session, content, route, client, or activation. +- Clinical content requires distinct source provenance, clinical review, patient-language + review, accessibility review, release authority, and rollback/retraction behavior. +- App-switcher/capture protection, protected storage, generic notifications, secure + deletion, logging redaction, accessibility semantics, and device compromise handling are + release gates rather than styling tasks. +- Patient communications must visibly state that urgent or emergency help is available + through the bedside call button or staff, not through delayed app messaging. + +## 6. Experience and visual direction + +Nightingale should feel calm, clear, and unhurried without hiding important information. +The supplied nightingale mark belongs in the app icon, welcome identity, and restrained +non-clinical empty/transition contexts. The seven images supplied under +`nightingale/backgrounds` are the product-directed background collection. The mark and +background collection must not decorate urgent instructions, obscure text, imply a +clinical outcome, identify a species to the patient, or substitute for status language. + +1. Start with reading comfort: system-or-larger text, readable cards, explicit timing + uncertainty, non-color status cues, images-off/high-contrast modes, and no essential + information embedded in imagery. +2. Use the seven governed Nightingale backgrounds only behind opaque or governed + high-contrast surfaces. Both platforms use the exact same metadata-stripped derivatives + and select the same stable image for each local Gregorian day using + `floorMod(epochDay, 7)`. There is no carousel, parallax, autonomous animation, or + clinical/state-driven image selection. +3. Use a simple three-domain navigation model only after patient-advisor validation: + **Today**, **My Path**, and **Care Team**. Messaging is reached only from an allowed + care-team or question flow, with a clear urgent-help escape route. +4. Treat every clinical label as reviewed content. Unknown vocabulary, incompatible + state vocabularies, stale projections, and withdrawn content must withhold rather than + invent a friendly explanation. + +## 7. Migration workstreams and acceptance criteria + +### Stream A — Product foundation and identity + +- [x] Create the isolated `codex/nightingale-patient-product` development stream from + `origin/main` at `446107ec`. +- [x] Inventory current staff and patient native targets, patient contracts, visual assets, + and existing safety evidence without changing production state. +- [x] Create the `nightingale/iosApp` and `nightingale/androidApp` application roots with + independent app IDs, build/test schemes, signing placeholders, and Debug/Release + configuration. App groups and protected-storage namespaces are deferred until a + Nightingale security architecture task introduces patient state. +- [x] Create `docs/nightingale/` and an index for future Nightingale-owned contract, + fixture, safety, and release documentation, with explicit lineage to the former patient + reference. +- [x] Establish an app-store and support naming checklist; reserve identifiers only through + authorized Apple/Google/organization processes. +- [x] Generate and mechanically verify the bounded foundation dependency inventory: + seven direct Android Release runtime declarations, 83 resolved components, 457 + dependency edges, zero iOS third-party packages, and four Apple system-module + imports, all bound to exact source hashes. This is not a standards-conformant SBOM, + vulnerability/provenance assessment, license review, or supply-chain approval. +- [x] Add dedicated Nightingale Android and iOS foundation CI jobs that verify source and + governance boundaries, dependency drift, Debug/Release tests and builds, native + simulator/emulator journeys, exact Release product identity, absence of networking + and Release test hooks, and the exact governed background set. Exact-SHA CI evidence + remains required for each published change. + +**Exit evidence:** both empty Nightingale targets build in Debug and Release, have no staff +source imports or endpoints, expose only the Nightingale name, and have a generated +dependency inventory. The bounded Stream A engineering exit evidence is present; external +identifier reservation, signing, distribution, and every live-feature gate remain separate +and open. No patient feature becomes enabled. + +### Stream B — Brand and icon implementation + +- [x] Replace the Hummingbird iOS staff AppIcon and Android launcher/adaptive/splash/login + derivatives with the supplied hummingbird artwork. +- [x] Create Nightingale iOS and Android icon sets from the supplied nightingale artwork, + including an iOS opaque 1024 px master and Android density/adaptive variants. +- [x] Add asset source, checksum, dimensions, background, crop/safe-zone, generation + command, and reviewer/rights fields to a provenance manifest. +- [x] Design and verify Android 13+ monochrome themed-icon variants for both products; do not + treat a full-color adaptive foreground as an accessible monochrome design. +- [x] Render both icons on actual iOS Simulator and Android API 35 emulator launchers; + inspect normal, dark, round/adaptive, and Android 12+ splash surfaces. +- [x] Verify repository-level notification, widget, extension, Live Activity, app-group, + shortcut, and generated-asset ownership; confirm no repository-owned store-listing + package exists; and prove source-predecessor Hummingbird in-place replacement with a + synthetic private-data canary on iOS and Android emulators. +- [x] Admit the seven user-supplied files under `nightingale/backgrounds` into a + Nightingale-owned decorative-background catalog: retain source filename, SHA-256, + dimensions, and byte-count lineage; commit exactly seven no-upscale, + metadata-stripped progressive JPEG derivatives; and mechanically reject missing, + additional, malformed, metadata-bearing, resized, or hash-drifted files. +- [x] Package the exact same seven derivatives in both native products and implement one + stable cross-platform selection per local Gregorian day. Keep the photos outside the + accessibility tree and input path; preserve text and actions when imagery is hidden; + suppress imagery for the patient preference and stronger supported system contrast/ + transparency settings; place all readable content on opaque or governed scrimmed + surfaces; and prohibit species, care-state, risk, urgency, or outcome semantics. +- [x] Verify the background foundation with native catalog/resource tests, iOS Simulator + visual inspection, Android API 35 semantic inspection, Release artifact hash/set + checks, Android `FLAG_SECURE` capture proof, and negative asset-verifier mutations. +- [ ] Record a durable source-archive location and applicable license/attribution evidence + for all seven supplied backgrounds before external, pilot, App Store, Play Store, + production, or marketing distribution. + _(2026-07-27: a checksum-bound v0 rights review now reconciles all seven catalog + sources, corroborates two exact provider pages and one current byte-for-byte provider + download, records five unresolved sources and 0/7 distribution-eligible assets, and + enforces the hold with 20 negative mutations. The parent item remains open because + no asset yet has the complete durable original/source/terms archive and named + release-owner approval.)_ +- [ ] Obtain named patient-advisor and accessibility review of the image set, crops, + legibility, comfort, cultural interpretation, and images-hidden behavior on supported + physical devices, text sizes, appearances, languages, and orientations. +- [ ] Verify retained released-artifact upgrades, distribution signing, monotonic + version/build values, external store records/screenshots, installed-widget persistence, + and actual notification rendering after those release surfaces exist and are approved. + +**Exit evidence:** platform tooling accepts every icon, all targets build Release, and +human visual review records correct brand, crop, contrast, and no unwanted transparency. + +### Stream C — Patient-safe application migration + +- [x] Classify and reimplement the first foundation subset only: lifecycle privacy cover, + Android secure-window protection, accessibility-aware decorative imagery, and a + released-content-only no-data state. All unlisted legacy sources remain held. +- [x] Document the pre-identity threat decisions for recovery, namespace isolation, + device compromise, local/remote logout ambiguity, deletion, backup/transfer, and + volatile input before introducing protected storage. +- [x] Classify and reimplement only the second foundation subset: a dormant, token-agnostic + iOS Keychain primitive, an Android Keystore AES-GCM primitive, explicit fail-closed + deletion semantics, backup/transfer exclusions, and lifecycle-cleared volatile inputs. + Tests use and remove synthetic canaries; production code has no caller. +- [x] Establish Nightingale contract ownership without creating an API: inventory all 23 + legacy paths/25 operations, trace the layered authorization controls, record each + operation's held disposition, and add a machine-verified `0.0.0-governance` artifact + with zero paths, no usable server/security scheme/client permission, and every + activation state false. +- [x] Classify the legacy patient-state vocabulary boundary without porting it. Reconcile + backend, contract, iOS, and Android coverage; record the 12-domain/49-pair backend + registry, native coverage gaps, category schema-placement conflict, Android Today + omissions, unknown-value differences, and the rejected absent-version compatibility + rule. All codes and labels remain held. +- [x] Define the first read-only encounter-access candidate without adding an operation: + remove legacy grant internals, restrict the held candidate to zero/one self inpatient + context, define a Nightingale-only opaque handle, document identity/source/audit/race + requirements, and mechanically verify 42 synthetic outcome cases plus negative + verifier mutations. The OpenAPI contract still has zero paths and both apps remain + offline. +- [x] Adopt and mechanically enforce the route/compatibility foundation: reserve + `/api/nightingale/v1` and the held `/inpatient-contexts` operation name, reject every + alias/proxy/redirect/fallback to legacy patient or staff APIs, keep route registration + prohibited, and add request-scoped default-deny identity/current-inpatient ports plus a + fail-closed precondition truth table. The foundation has no route/controller/provider + binding, database query, OpenAPI operation, or native client. +- [x] Complete the non-runnable identity/session/recovery and current-inpatient-source + candidate designs without choosing a provider or adapter: reconcile exact legacy source + hashes, define self-only and fail-closed state semantics, prohibit guessed freshness + thresholds, and mechanically pin 64 identity plus 42 source cases with nine adversarial + verifier mutations. Every activation, provider, credential, route, query, client, + representative, patient-access, and production field remains disabled, null, or held. +- [x] Complete the bounded source-by-source identity-input, enrollment/recovery, first-read, + and error/non-disclosure slice: classify and SHA-256 pin 65 exact contract, backend, + database, iOS, Android, and test files; document the missing recovery workflow, + unsafe first-record selection, cross-platform absence/error divergence, random UUID + substitution, and server-message passthrough; and add a negative verifier that keeps + all runtime, route, provider, credential-migration, production, and patient-creation + permissions false. +- [x] Complete the bounded communication and notification source-classification slice: + classify and SHA-256 pin 130 exact contract, backend, database, patient-native, + staff-native/web, and test files; trace patient acceptance through staff routing and + readback; document absent patient push and automatic refresh, ambiguous-retry + duplication, delivery-wording overstatement, iOS escalation decode failure, Android + state-label gaps, and ungoverned locale/copy; and add negative enforcement for zero + runtime, route, provider, channel, payload, polling, offline-queue, production, and + patient-creation permissions. +- [x] Classify each existing Hummingbird Patient source file as reusable safety primitive, + reusable product behavior, test/fixture-only, or rejected legacy behavior. The + machine-checked universe contains 256 tracked product sources: 122 unique sources + covered by the two prior ledgers plus a final 134-source journey, preference, + presentation, synthetic/debug, persistence, and release ledger. The union covers all + 256 sources with exact path-list and byte checksums. Classification completeness does + not approve any source for migration or runtime adoption. +- [x] Correct the mainline iOS release-path collision without repurposing the legacy + reference app: restore its exact Hummingbird Patient bundle/display identities, move + the Nightingale export policy to `nightingale/iosApp`, map the Apple registry only to + the independent Nightingale project/scheme, reject future collisions in CI, and + verify exported IPA identity/build metadata with one positive and four negative + mutations. Signing, upload, store acceptance, tester distribution, and pilot + authorization remain open. +- [ ] Port primitives into Nightingale by reviewed commits, not a blind directory copy: + patient API boundary, protected storage, lifecycle/screen-capture privacy cover, + accessibility presentation preferences, patient-safe vocabulary, and volatile drafts. + Protected storage, lifecycle/screen-capture protection, the foundation accessibility + presentation subset, device-local reduced-motion and decorative-imagery preferences, + volatile-input foundations, and an empty contract governance boundary are complete. + Patient API runtime behavior and all vocabulary code/copy remain held. +- [x] Reissue all product strings, accessibility IDs, test hooks, storage keys, telemetry + event names, and diagnostics under the Nightingale namespace. + The deterministic namespace manifest now binds 12 exact sources and inventories ten + shared accessibility IDs, four iOS Debug hooks, ten fully qualified persistent + identifiers, zero telemetry events, and zero diagnostic channels. Its independent + verifier reproduces the manifest byte-for-byte, rejects eight mutations, rejects + legacy patient tokens and unregistered logging/analytics primitives, and is enforced + by contract and native CI. This closes only the identifiers implemented by the + current offline foundation; every future string, event, channel, or identifier must + extend the governed inventory. +- [x] Require a compile-time scan proving Nightingale has no `hummingbird.patient` package, + bundle, endpoint, storage, or user-facing string except explicit migration provenance. +- [ ] Preserve the reference app untouched until the Nightingale migration evidence is + independently approved; then retire it through a separately authorized change. + +**Exit evidence:** Nightingale Debug/Release binaries contain no staff capability, endpoint, +or legacy patient namespace; iOS and Android migration suites pass equivalent safety tests. + +### Stream D — Patient journeys and content controls + +- [x] Define the first non-runnable Today projection candidate without adding an + operation: use the held opaque inpatient-context handle; require a governed context + on every patient-visible value; distinguish released, released-empty, and + not-available sections; define correction/retraction and generic non-disclosure + behavior; and mechanically verify 68 synthetic outcomes plus 24 adversarial + mutations. The executable contract still has zero paths and every activation remains + false. +- [ ] Implement and validate released Today, My Path, Care Team, education/teach-back, + discharge preparation, and capability-scoped communication only from approved projection + contracts. +- [x] Add a held, non-runnable reference catalog for admission, transfer, procedure, + delayed test/result, pre-rounds question, handoff, changed discharge estimate, + discharge mid-thread, identity correction, representative limits, language/interpreter, + accommodations, sensitive-data denial, source outage/staleness, and content + retraction/correction. The machine-verified catalog contains all 15 required families, + 27 synthetic cases, 12 exact evidence-source checksums, and 23 adversarial mutations; + it adds no operation and keeps every runtime, representative, clinical release, + communication, notification, database, production, and deployment permission false. +- [x] Prove generic non-disclosure for unknown, revoked, expired, cross-principal, + wrong-encounter, and omitted-resource requests. The route-free Nightingale domain + gate returns one exact identifier-free 404/code/cache tuple for all 39 + non-disclosable combinations in its exhaustive 40-row truth table; the sole positive + row only continues to later governed projection evaluation. There is still no route, + query, patient content, or authorization. +- [x] Keep clinical approval, content release, feature activation, pilot enrollment, and + source-connector deployment separate and default-off. Five distinct state types, + code-owned negative configuration, five false executable-contract facts, runtime + non-registration scans, and an exhaustive 32-row truth table now enforce the + separation. Thirty-one incomplete combinations hold; the all-positive combination + only continues to later operation-specific release evaluation and does not authorize + identity, disclosure, production, or pilot use. + +**Exit evidence:** every displayed field has an approved source/release/provenance/freshness/ +uncertainty/correction/translation/offline rule, and every mutation has deterministic +authorization, idempotency, audit, and patient-visible outcome evidence. + +### Stream E — Accessibility, privacy, and human validation + +- [x] Reissue and rerun the foundation subset of automated privacy controls under Nightingale + identities: lifecycle cover, Android `FLAG_SECURE`, no-network boundary, large-text image + attenuation, iOS high-contrast image withholding, an Android high-contrast policy seam, + and patient-safe accessibility semantics. +- [x] Implement and rerun the bounded display-comfort subset under Nightingale identities: + separate device-local preferences from future account preferences, apply the stronger + system/patient reduced-motion choice to every current motion site, allow decorative + imagery to be disabled without hiding essential content, persist both choices across + relaunch, and bind native tests to exact local Debug/Release artifacts. +- [x] Complete the bounded foundation accessibility-layout matrix: correct the failing iOS + dark-background accent, add explicit Android light/dark schemes, mechanically prove + every current semantic text pair at 4.5:1 or greater, preserve ordered landmarks, + make both controls scroll-reachable at iOS accessibility XXXL and Android font scale + `2.0` in landscape, and prove 44-point iOS plus 48 dp Android targets. This closes + only the current offline shell; future screens, manual assistive-technology review, + language/RTL coverage, full WCAG conformance, and named approvals remain open. +- [x] Complete the bounded offline-shell accessibility/language-readiness subset: move all + 15 nonclinical strings into exact, reconciled native catalogs; expose exactly three + ordered headings; use two restrained status-announcement paths; exercise iOS rendered + double length plus Debug-only RTL layout and Android Debug `en-XA`/`ar-XB`; and reject + pseudolocales from both Release artifacts. This is readiness evidence only. No + translation, human VoiceOver/TalkBack review, full WCAG conformance, or future-screen + accessibility is approved. +- [x] Integrate the governed seven-photo Nightingale background catalog into the bounded + offline shell without making any image essential: cross-platform local-day selection, + deterministic packaging, strong scrims/cards, images-hidden and high-contrast/ + reduced-transparency suppression, no motion loop, no accessibility announcement, and + exact derivative hashes are mechanically and natively tested. +- [x] Establish the draft foundation threat and clinical-hazard model: version current and + future trust boundaries, 27 implemented-control claims with their limits, 22 + security/privacy threats, 22 clinical hazards, 18 abuse cases, 20 activation gates, + verification/incident requirements, platform asymmetries, and 17 open risks; bind it + mechanically to the unchanged default-off foundation with 11 negative self-tests. + This is engineering governance, not a safety case, compliance claim, residual-risk + acceptance, penetration test, tabletop, or named approval. +- [x] Carry forward the existing automated privacy controls only as candidate evidence and + rerun them under Nightingale app IDs and assets: package and exactly verify an iOS + offline privacy manifest, declare only app-local `UserDefaults` reason `CA92.1`, + explicitly deny Android cleartext and user-added/debug trust while retaining no + network permission and no backup/transfer, reject runtime networking/logging/tracking/ + clipboard primitives, and inspect installed and Release artifacts on both platforms. + Store declarations, future connected-product data flows, privacy approval, signed + distribution, and penetration testing remain open. +- [ ] Meet WCAG 2.2 AA plus iOS/Android guidance for screen-reader navigation, focus order, + headings, labels/actions, live-region restraint, target size, system text, high contrast, + motion, landscape where supported, language expansion, captions/transcripts, and images + disabled. +- [ ] Complete independent patient-advisor, accessibility, privacy/security, clinical + safety, language/interpreter, legal/HIM, nursing, medical-staff, pharmacy, and support + review. Automated checks never substitute for these approvals. +- [ ] Obtain independent clinical-safety/privacy/security ratification of the foundation + threat/hazard model and run red-team, tabletop, penetration, mobile security, + dependency/SBOM, secrets, and release-readiness reviews. + +**Exit evidence:** named reviewers sign the current matrices; resolved risks and explicit +residual risks are versioned; pilot criteria remain independently ratified. + +### Stream F — Controlled pilot and scale + +- [x] Define facility/unit/cohort/language/exclusion/support-hour configuration with a + default-off, audited, expiry-bound manifest. The generated candidate contains an + empty committed template; opaque facility/unit/cohort handles; non-automatic + enrollment with a 25-principal technical ceiling; canonical locale tags and released + interpreter coverage; fail-closed exclusion results; IANA-zone weekly support + windows; a seven-day validity ceiling; seven named approval roles; nine append-only, + content-free audit event types; rollback and default-engaged kill-switch evidence; + and 34 synthetic cases with 25 adversarial verifier mutations. Thirty-three cases + hold; the only complete synthetic case may proceed solely to an external go/no-go + review. There is no runtime evaluator, caller, route, patient enrollment, production + query, disclosure, deployment, or activation. +- [ ] Complete identity proofing, recovery, representatives, consent, sensitive-service, + device, push, retention, incident, kill-switch, downtime, and support procedures. +- [ ] Run production-like integration, load, failover, EHR outage, push outage, recovery, + monitoring, and rollback exercises before any patient activation. +- [ ] Authorize one controlled pilot through named governance owners; deploy using the + protected Zephyrus release workflow only after the approved Nightingale artifact is + merged and exact-SHA CI passes. + +**Exit evidence:** a signed go/no-go record, verified rollback, production deployment +manifest, and clinical/privacy/security/accessibility approvals. This is intentionally not +part of application development work. + +## 8. Verification matrix + +| Layer | Required evidence before a Nightingale milestone is checked | +| ---------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| Laravel/contract | route inventory, contract validation, authorization/IDOR/non-disclosure tests, fixtures, database migration review, and no activation state change | +| iOS | generated project drift check, unit tests, iPhone Simulator UI/accessibility journeys, Debug and Release builds, binary namespace/debug-hook scan, icon visual review | +| Android | Gradle boundary check, unit tests, API 35 emulator instrumentation/accessibility journeys, Debug and Release builds, APK namespace/debug-hook scan, launcher/splash icon review | +| Product safety | checklist entry, source/evidence links, state of clinical/content/privacy/identity approvals, residual-risk statement, and rollback relevance | +| Release | clean protected `main`, approved review, exact-SHA green CI, `./deploy.sh --check`, then `./deploy.sh`; migrations are separate path-scoped, backed-up approvals | + +## 9. Immediate implementation sequence + +1. [x] Define and mechanically pin the route/compatibility ADR, independent product + namespace, held candidate path, and default-deny identity/source prerequisite ports. The + contract remains at zero paths and neither route registration nor native networking is + enabled. +2. [x] Complete held, non-runnable identity/session/recovery and authoritative + current-inpatient-source state designs and exhaustive synthetic fixtures. The result + defines 106 cases and negative enforcement but deliberately selects no provider, + credential, representative authority, adapter, query, or freshness threshold. +3. [ ] Obtain named independent approvals for identity, proofing, enrollment, session, + recovery, representatives, current-inpatient source, cohort, lifecycle, freshness, + linkage, audit, support, and rollback. Only then implement default-off non-production + adapters behind the existing ports; do not query a patient source before approval. +4. [x] Complete the repository-level cross-surface brand audit for notification, widget, + extension, Live Activity, app group, shortcut, store-package absence, and + source-predecessor installed upgrade. The audit passes on clean iOS and Android + emulators while explicitly withholding released-artifact, distribution-signing, + store-console, installed-widget, and actual-notification-rendering claims. +5. [x] Complete source-by-source classification for identity input, enrollment/recovery, + first-read projection models, and error/non-disclosure handling. The 65-file, + checksum-pinned ledger and negative verifier treat the legacy contract, tests, + candidate fixtures, and vocabulary as evidence, not approved implementation; no + runtime code, route, provider, production query, or patient was added. +6. [x] Update this checklist and its devlog for the verified 65-file classification slice. + Clinical, privacy, accessibility, identity, source, pilot, and deployment completion + remain explicitly open. +7. [x] Complete the bounded 130-file patient communication, staff-handoff, native + experience, notification registration/delivery, urgency/offline, and error + classification. Mechanically pin the absent patient push and automatic-refresh + implementations, human-retry identity loss, acceptance-versus-delivery distinction, + iOS escalation-state contract mismatch, Android rendering gaps, and the false + staff-close decode hypothesis. +8. [x] Update the master checklist, migration index, authorization matrix, documentation + index, execution log, and CI for the verified communication/notification slice while + leaving every Nightingale operation, network client, notification provider, patient + mutation, production query, and production activation disabled. +9. [x] Close the remaining 134-source journey, preference, accessibility presentation, + synthetic/debug, persistence, and release-classification slice. Mechanically prove + full 256-source universe coverage; pin direct top-level Messages navigation, unsafe + first-record selection, iOS aggregate-context drift, Android composite-path context + drift, preference-surface mismatch, semantically inert Android reduced motion, + test-only release exclusion, rejected deployed reference provisioning, and + pathway-only two-person release coverage. +10. [x] Update the checklist, migration and authorization records, documentation index, + execution log, and CI for complete source classification; rerun the independent + Nightingale iOS and Android emulator suites and Release builds; and leave every + implementation, network, source, mutation, synthetic runtime, production, migration, + release, deployment, and patient-creation permission false. +11. [x] Define a held, non-runnable Today projection candidate with a Nightingale-owned + path and opaque handles, eight explicit section states, field-level release/ + freshness/uncertainty/language/correction/offline contexts, bounded error and audit + semantics, 68 synthetic outcomes, 14 direct-source checksums, and 24 negative + verifier mutations. Do not add the path to OpenAPI or Laravel. +12. [ ] Obtain named identity/source and field-level clinical/content/language/privacy/ + accessibility approvals before implementing any Today route, adapter, projection + query, generated client, or native rendering. Emulator verification of the unchanged + offline foundation does not satisfy these approvals. +13. [x] Implement the approved offline presentation-preferences foundation on both native + platforms: two Nightingale-only device preferences, stronger-system-setting + precedence, effective reduced motion, decorative-imagery suppression, relaunch + persistence, Release-artifact boundary scans, and clean iOS Simulator/Android API 35 + suites. This does not close full accessibility conformance or any human approval. +14. [x] Audit and harden the current foundation at maximum text, landscape, dark appearance, + and increased contrast: replace the failing fixed iOS accent with an appearance-aware + palette, add Android system-selected dark colors, correct semantic reading order, + expand complete interaction rows, declare both iOS landscape orientations, require + actual landscape application geometry, add native contrast/target/reflow tests, and + bind the result to exact local Debug/Release artifacts. The broader Stream E + conformance and independent-review items remain open. +15. [x] Establish and mechanically enforce the draft foundation threat and clinical-hazard + model. It separates current offline exposure from future activation risk, keeps all + high/critical live-data hazards disabled, records no approval or accepted residual + risk, and leaves independent ratification, red-team/tabletop, penetration, signed + artifact, non-production integration, pilot, and release evidence open. +16. [x] Generate and mechanically enforce the foundation dependency inventory. Resolve the + Android Release runtime graph through Gradle’s structured resolution model, bind + seven declarations/83 components/457 edges and the zero-package iOS application + target to source hashes, and explicitly withhold standards, vulnerability, + provenance, license, signing, artifact, distribution, and supply-chain approval + claims. +17. [x] Govern and integrate the seven supplied Nightingale background images across both + native foundations. Preserve source lineage, commit only exact metadata-stripped + derivatives, use the same local-Gregorian-epoch-day catalog index on iOS and + Android, keep imagery decorative and suppressible, enforce readable surfaces and + exact Release packaging, and validate on iOS Simulator plus Android API 35. + Distribution rights/attribution, physical-device review, patient-advisor review, + and exact-SHA publication evidence remain separate gates. +18. [x] Complete the bounded offline privacy/release-control carry-forward under Nightingale + identity. Package and parse the exact iOS privacy manifest, require only app-local + `UserDefaults` reason `CA92.1`, declare no current-foundation tracking or collected + data, add Android cleartext denial with system-only trust and no debug override, + prove no Android network permission or backup, reject networking/logging/tracking/ + clipboard primitives, and inspect the installed apps and Release artifacts. This + remains candidate engineering evidence; any live identity, data, telemetry, + networking, store declaration, signed distribution, or privacy approval is held. +19. [x] Reconcile and fulfill the separately requested production sample-patient objective. + The first authorized review was read-only and found one legacy Hummingbird reference + foundation. A later explicit direction required a Nightingale sample using that + deprecated product's sample as the template. Two serializable, advisory-locked, + cardinality-checked transactions created exactly one Nightingale-owned operational + synthetic encounter and one fresh UUIDv7 patient principal. Only principal type, + locale/timezone, unit, acuity, and safe lifecycle shape were carried forward. + Credentials, sessions, challenges, grants, identity links, digests, encrypted + references, external identifiers, projections, and content were not copied. The + Nightingale principal remains pending/inactive and the Hummingbird template remains + unchanged. Exact evidence is retained under + `docs/evidence/nightingale/production-sample-patient-2026-07-27/`. +20. [x] Close the current offline shell's bounded accessibility/language-readiness gap. + Govern 15 exact cross-platform English strings, three headings, and two restrained + status-announcement paths; pass iOS double-length/RTL and Android `en-XA`/`ar-XB` + emulator journeys; verify Debug-only test behavior and English-only Release + packaging; and retain non-PHI screenshots/hierarchies. Keep every translation, + human assistive-technology review, full WCAG claim, live screen, identity/source + approval, patient capability, distribution, pilot, and production gate open. +21. [x] Reconcile current `main` without allowing its legacy iOS release-path collision to + redefine Nightingale. Restore the reference Hummingbird Patient project to its + exact prior identity, move the export policy and registry mapping to + `nightingale/iosApp`, add fail-closed registry/export/IPA checks, and revalidate + both checksum-pinned migration ledgers plus the dependent Today evidence against + the corrected 256-source universe. Do not sign, upload, distribute, activate, or + claim store readiness. +22. [x] Define the controlled-pilot configuration boundary without creating a pilot + runtime. Generate an exact default-off manifest for facility, unit, cohort, + language, exclusion, support-hour, validity, prerequisite, named-approval, audit, + rollback, and kill-switch records. Require a non-null UTC start/expiry no more than + 168 hours apart before even external go/no-go review; cap the technical cohort at + 25; prohibit automatic enrollment, unapproved language fallback, exclusion + inference, PHI/content-bearing audit events, post-expiry activity, and runtime + activation; and verify 34 synthetic cases plus 25 negative mutations. Keep the + committed template empty, all executable activation facts false, and the sole + structurally complete synthetic case limited to external review. +23. [x] Convert the background-rights prose hold into a per-asset, checksum-bound, + fail-closed review queue. Reconcile all seven source filenames, dimensions, and + hashes; corroborate the two filename-identified Unsplash asset pages; prove one + current provider download is byte-for-byte identical and classify the other as a + resized/re-encoded source relation; leave five exact sources unresolved; require + an organization-controlled original/source/terms archive plus attribution and + named release-owner review for every image; and reject 20 invented-approval, + archive, identity, hash, count, and eligibility mutations. This creates no legal + determination and closes no parent release-rights item: 0/7 assets remain + distribution eligible. + +### 9.1 Current-main collision checkpoint (non-milestone) + +After the 41/54 namespace-and-activation slice passed exact-SHA CI, `main` acquired a +second rename that attempted to make the deprecated Hummingbird Patient source roots and +backend configuration claim the Nightingale identity. The feature branch merged current +`main`, preserved its unrelated Flow 4D planning update, and forward-reverted only that +incompatible rename. Exact tree comparison proves all product-boundary paths match the +last verified independent-product baseline, while the unrelated upstream document matches +its `main` version. + +The complete contract/backend/native acceptance chain was rerun, including 23 Laravel +tests/149 assertions, iOS 11/11 unit and 6/6 UI tests on an iPhone 16e simulator, Android +8/8 Debug and 8/8 Release unit tests, and 10/10 installed journeys on a separate read-only +API 35 emulator. The evidence is retained in +`docs/evidence/nightingale/current-main-reconciliation-2026-07-27/`. + +This checkpoint receives no new checkbox or percentage credit because it restores an +already-completed architecture rather than adding function. The master checklist remains +41/54 complete (75.93%); all 13 open gates remain open. + +### 9.2 Controlled-pilot manifest checkpoint + +The first Stream F item is now complete as a non-runnable governance boundary. The +generated source of record is +`docs/nightingale/pilot/candidates/v0/candidate.json`, with its synthetic evaluation matrix +in the adjacent `fixtures.json`. The candidate defines every required configuration +domain while committing no real facility, unit, cohort, language, support schedule, +approval, release, deployment, or pilot identifier. + +The field-level decision record is +`docs/nightingale/CONTROLLED-PILOT-MANIFEST-FOUNDATION-2026-07-27.md`; exact contract, +backend, iOS Simulator, Android emulator, artifact, and safety evidence is retained under +`docs/evidence/nightingale/controlled-pilot-manifest-2026-07-27/`. + +The independent verifier reproduces both generated artifacts exactly, binds four current +activation/foundation sources by SHA-256, validates the empty executable contract and +code-owned negative backend defaults, evaluates 34 synthetic cases, and exercises 25 +negative mutations. Exactly 33 cases hold. The one structurally complete synthetic case +returns only `eligible_for_external_go_no_go_review_only`, which is expressly neither +activation nor enrollment, deployment, disclosure, or production authorization. + +This closes one configuration-definition item and moves the master checklist to 42/54 +(77.78%), leaving 12/54 open. All real approvals, identity/source implementation, +patient-visible operations, human validation, signed distribution, production-like +exercises, pilot authorization, merge, and deployment remain open. + +### 9.3 Background rights-review checkpoint + +The background distribution hold is now an exact review queue rather than an +unstructured future task. The machine-readable v0 record is +`nightingale/backgrounds/rights/rights-review.v0.json`; the detailed evidence standard, +per-asset reconciliation, archive requirements, closure procedure, and residual risks are +in +`docs/nightingale/BACKGROUND-RIGHTS-AND-SOURCE-ARCHIVE-REVIEW-2026-07-27.md`. + +The record binds all seven source identities to the immutable catalog lineage. +Provider-asset IDs in two filenames reconcile to exact Unsplash pages and creators. One +current provider download equals the catalog source binary; the other is corroborated as +the same resized/re-encoded image. Five exact sources remain unresolved. No image has the +required durable organization-controlled original/source/terms archive or named +release-owner decision, so all seven remain distribution-ineligible. + +The independent verifier is integrated into the contract CI and native product boundary. +It checks the catalog digest, exact per-asset records, 7/2/1/5/0 summary, global hold, and +20 negative mutations. It cannot issue approval. A reviewed, versioned successor record +is required before the catalog distribution state can change. + +This completes immediate milestone 23 only. It does not close a parent checkbox or change +the 42/54 (77.78%) count. + +## 10. Explicit holds + +- No further production database access for patient creation, fixture insertion, identity + enrollment, data correction, or feature activation belongs to this stream without a new + explicit authorization. The bounded 2026-07-27 exception created only the documented + synthetic encounter and pending/inactive principal. +- The production credential supplied by the operator is runtime-only. It is not committed, + copied into source/documentation/fixtures/tests, or treated as an application secret + source. +- One Nightingale synthetic sample exists, but it is not identity-linked, login-capable, + encounter-authorized, content-bearing, routed, enrolled, activated, or a real patient. + No patient feature, migration, or production application release occurs from this plan + alone. +- No staff Hummingbird code is copied into Nightingale, and no patient code is made a staff + Hummingbird runtime mode. diff --git a/hummingbird/androidApp/app/src/main/res/mipmap-anydpi-v33/ic_launcher.xml b/hummingbird/androidApp/app/src/main/res/mipmap-anydpi-v33/ic_launcher.xml new file mode 100644 index 00000000..1451b6a0 --- /dev/null +++ b/hummingbird/androidApp/app/src/main/res/mipmap-anydpi-v33/ic_launcher.xml @@ -0,0 +1,6 @@ + + + + + + diff --git a/hummingbird/androidApp/app/src/main/res/mipmap-anydpi-v33/ic_launcher_round.xml b/hummingbird/androidApp/app/src/main/res/mipmap-anydpi-v33/ic_launcher_round.xml new file mode 100644 index 00000000..1451b6a0 --- /dev/null +++ b/hummingbird/androidApp/app/src/main/res/mipmap-anydpi-v33/ic_launcher_round.xml @@ -0,0 +1,6 @@ + + + + + + diff --git a/hummingbird/androidApp/app/src/main/res/mipmap-hdpi/ic_launcher.png b/hummingbird/androidApp/app/src/main/res/mipmap-hdpi/ic_launcher.png index 733e2b95..7452df9d 100644 Binary files a/hummingbird/androidApp/app/src/main/res/mipmap-hdpi/ic_launcher.png and b/hummingbird/androidApp/app/src/main/res/mipmap-hdpi/ic_launcher.png differ diff --git a/hummingbird/androidApp/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png b/hummingbird/androidApp/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png index 3ff5c684..be1589f8 100644 Binary files a/hummingbird/androidApp/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png and b/hummingbird/androidApp/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png differ diff --git a/hummingbird/androidApp/app/src/main/res/mipmap-hdpi/ic_launcher_monochrome.png b/hummingbird/androidApp/app/src/main/res/mipmap-hdpi/ic_launcher_monochrome.png new file mode 100644 index 00000000..480068ed Binary files /dev/null and b/hummingbird/androidApp/app/src/main/res/mipmap-hdpi/ic_launcher_monochrome.png differ diff --git a/hummingbird/androidApp/app/src/main/res/mipmap-hdpi/ic_launcher_round.png b/hummingbird/androidApp/app/src/main/res/mipmap-hdpi/ic_launcher_round.png index 19001fbc..7452df9d 100644 Binary files a/hummingbird/androidApp/app/src/main/res/mipmap-hdpi/ic_launcher_round.png and b/hummingbird/androidApp/app/src/main/res/mipmap-hdpi/ic_launcher_round.png differ diff --git a/hummingbird/androidApp/app/src/main/res/mipmap-mdpi/ic_launcher.png b/hummingbird/androidApp/app/src/main/res/mipmap-mdpi/ic_launcher.png index 5342773b..72b26a00 100644 Binary files a/hummingbird/androidApp/app/src/main/res/mipmap-mdpi/ic_launcher.png and b/hummingbird/androidApp/app/src/main/res/mipmap-mdpi/ic_launcher.png differ diff --git a/hummingbird/androidApp/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png b/hummingbird/androidApp/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png index df1e65b0..c10adfe4 100644 Binary files a/hummingbird/androidApp/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png and b/hummingbird/androidApp/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png differ diff --git a/hummingbird/androidApp/app/src/main/res/mipmap-mdpi/ic_launcher_monochrome.png b/hummingbird/androidApp/app/src/main/res/mipmap-mdpi/ic_launcher_monochrome.png new file mode 100644 index 00000000..1f584f50 Binary files /dev/null and b/hummingbird/androidApp/app/src/main/res/mipmap-mdpi/ic_launcher_monochrome.png differ diff --git a/hummingbird/androidApp/app/src/main/res/mipmap-mdpi/ic_launcher_round.png b/hummingbird/androidApp/app/src/main/res/mipmap-mdpi/ic_launcher_round.png index d7fa793b..72b26a00 100644 Binary files a/hummingbird/androidApp/app/src/main/res/mipmap-mdpi/ic_launcher_round.png and b/hummingbird/androidApp/app/src/main/res/mipmap-mdpi/ic_launcher_round.png differ diff --git a/hummingbird/androidApp/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/hummingbird/androidApp/app/src/main/res/mipmap-xhdpi/ic_launcher.png index 90fbec3a..590552bf 100644 Binary files a/hummingbird/androidApp/app/src/main/res/mipmap-xhdpi/ic_launcher.png and b/hummingbird/androidApp/app/src/main/res/mipmap-xhdpi/ic_launcher.png differ diff --git a/hummingbird/androidApp/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png b/hummingbird/androidApp/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png index 91b6206e..baaa0906 100644 Binary files a/hummingbird/androidApp/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png and b/hummingbird/androidApp/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png differ diff --git a/hummingbird/androidApp/app/src/main/res/mipmap-xhdpi/ic_launcher_monochrome.png b/hummingbird/androidApp/app/src/main/res/mipmap-xhdpi/ic_launcher_monochrome.png new file mode 100644 index 00000000..19c4d554 Binary files /dev/null and b/hummingbird/androidApp/app/src/main/res/mipmap-xhdpi/ic_launcher_monochrome.png differ diff --git a/hummingbird/androidApp/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png b/hummingbird/androidApp/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png index 6a77d5ad..590552bf 100644 Binary files a/hummingbird/androidApp/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png and b/hummingbird/androidApp/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png differ diff --git a/hummingbird/androidApp/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/hummingbird/androidApp/app/src/main/res/mipmap-xxhdpi/ic_launcher.png index 4b50ea0d..a059bc64 100644 Binary files a/hummingbird/androidApp/app/src/main/res/mipmap-xxhdpi/ic_launcher.png and b/hummingbird/androidApp/app/src/main/res/mipmap-xxhdpi/ic_launcher.png differ diff --git a/hummingbird/androidApp/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png b/hummingbird/androidApp/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png index 0fee28e1..2b8a175e 100644 Binary files a/hummingbird/androidApp/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png and b/hummingbird/androidApp/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png differ diff --git a/hummingbird/androidApp/app/src/main/res/mipmap-xxhdpi/ic_launcher_monochrome.png b/hummingbird/androidApp/app/src/main/res/mipmap-xxhdpi/ic_launcher_monochrome.png new file mode 100644 index 00000000..88ae9863 Binary files /dev/null and b/hummingbird/androidApp/app/src/main/res/mipmap-xxhdpi/ic_launcher_monochrome.png differ diff --git a/hummingbird/androidApp/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png b/hummingbird/androidApp/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png index 702e1d49..a059bc64 100644 Binary files a/hummingbird/androidApp/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png and b/hummingbird/androidApp/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png differ diff --git a/hummingbird/androidApp/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/hummingbird/androidApp/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png index 9aa713fa..2851c0f7 100644 Binary files a/hummingbird/androidApp/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png and b/hummingbird/androidApp/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png differ diff --git a/hummingbird/androidApp/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png b/hummingbird/androidApp/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png index 67cb88bc..3c067238 100644 Binary files a/hummingbird/androidApp/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png and b/hummingbird/androidApp/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png differ diff --git a/hummingbird/androidApp/app/src/main/res/mipmap-xxxhdpi/ic_launcher_monochrome.png b/hummingbird/androidApp/app/src/main/res/mipmap-xxxhdpi/ic_launcher_monochrome.png new file mode 100644 index 00000000..0a3e985b Binary files /dev/null and b/hummingbird/androidApp/app/src/main/res/mipmap-xxxhdpi/ic_launcher_monochrome.png differ diff --git a/hummingbird/androidApp/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png b/hummingbird/androidApp/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png index 261894e1..2851c0f7 100644 Binary files a/hummingbird/androidApp/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png and b/hummingbird/androidApp/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png differ diff --git a/hummingbird/androidPatientApp/app/build.gradle.kts b/hummingbird/androidPatientApp/app/build.gradle.kts index 30c0a786..becf9e5b 100644 --- a/hummingbird/androidPatientApp/app/build.gradle.kts +++ b/hummingbird/androidPatientApp/app/build.gradle.kts @@ -4,17 +4,17 @@ plugins { id("org.jetbrains.kotlin.plugin.compose") } -val patientApiEnabled = providers.gradleProperty("nightingale.api.enabled") +val patientApiEnabled = providers.gradleProperty("hummingbird.patient.api.enabled") .orElse("false") -val patientApiBaseUrl = providers.gradleProperty("nightingale.api.baseUrl") +val patientApiBaseUrl = providers.gradleProperty("hummingbird.patient.api.baseUrl") .orElse("https://zephyrus.acumenus.net") android { - namespace = "net.acumenus.nightingale" + namespace = "net.acumenus.hummingbird.patient" compileSdk = 35 defaultConfig { - applicationId = "net.acumenus.nightingale" + applicationId = "net.acumenus.hummingbird.patient" minSdk = 26 targetSdk = 35 versionCode = 1 @@ -115,9 +115,9 @@ tasks.register("verifyPatientProductBoundary") { } val volatileInputFiles = listOf( - "src/main/java/net/acumenus/nightingale/ui/PatientAuthenticationScreen.kt", - "src/main/java/net/acumenus/nightingale/ui/PatientMessagingPanel.kt", - "src/main/java/net/acumenus/nightingale/ui/PatientSessionManagementScreen.kt", + "src/main/java/net/acumenus/hummingbird/patient/ui/PatientAuthenticationScreen.kt", + "src/main/java/net/acumenus/hummingbird/patient/ui/PatientMessagingPanel.kt", + "src/main/java/net/acumenus/hummingbird/patient/ui/PatientSessionManagementScreen.kt", ).map(projectDir::resolve) val forbiddenSavedStateSymbols = listOf( "remember" + "Saveable", diff --git a/hummingbird/androidPatientApp/app/src/androidTest/java/net/acumenus/nightingale/PatientAuthenticationSmokeTest.kt b/hummingbird/androidPatientApp/app/src/androidTest/java/net/acumenus/hummingbird/patient/PatientAuthenticationSmokeTest.kt similarity index 87% rename from hummingbird/androidPatientApp/app/src/androidTest/java/net/acumenus/nightingale/PatientAuthenticationSmokeTest.kt rename to hummingbird/androidPatientApp/app/src/androidTest/java/net/acumenus/hummingbird/patient/PatientAuthenticationSmokeTest.kt index 4ade8251..5db082f3 100644 --- a/hummingbird/androidPatientApp/app/src/androidTest/java/net/acumenus/nightingale/PatientAuthenticationSmokeTest.kt +++ b/hummingbird/androidPatientApp/app/src/androidTest/java/net/acumenus/hummingbird/patient/PatientAuthenticationSmokeTest.kt @@ -1,4 +1,4 @@ -package net.acumenus.nightingale +package net.acumenus.hummingbird.patient import androidx.compose.ui.test.assertIsDisplayed import androidx.compose.ui.test.junit4.createAndroidComposeRule @@ -13,7 +13,7 @@ class PatientAuthenticationSmokeTest { @Test fun signedOutShellExplainsTheSeparatePatientBoundary() { - composeRule.onNodeWithText("Nightingale").assertIsDisplayed() + composeRule.onNodeWithText("Hummingbird Patient").assertIsDisplayed() composeRule.onNodeWithText("A separate patient account") .performScrollTo() .assertIsDisplayed() diff --git a/hummingbird/androidPatientApp/app/src/androidTest/java/net/acumenus/nightingale/PatientPrimaryJourneyInstrumentedTest.kt b/hummingbird/androidPatientApp/app/src/androidTest/java/net/acumenus/hummingbird/patient/PatientPrimaryJourneyInstrumentedTest.kt similarity index 96% rename from hummingbird/androidPatientApp/app/src/androidTest/java/net/acumenus/nightingale/PatientPrimaryJourneyInstrumentedTest.kt rename to hummingbird/androidPatientApp/app/src/androidTest/java/net/acumenus/hummingbird/patient/PatientPrimaryJourneyInstrumentedTest.kt index 5edb958f..005fafac 100644 --- a/hummingbird/androidPatientApp/app/src/androidTest/java/net/acumenus/nightingale/PatientPrimaryJourneyInstrumentedTest.kt +++ b/hummingbird/androidPatientApp/app/src/androidTest/java/net/acumenus/hummingbird/patient/PatientPrimaryJourneyInstrumentedTest.kt @@ -1,4 +1,4 @@ -package net.acumenus.nightingale +package net.acumenus.hummingbird.patient import android.content.Context import android.content.Intent @@ -17,7 +17,7 @@ import androidx.compose.ui.test.performTextInput import androidx.test.core.app.ActivityScenario import androidx.test.core.app.ApplicationProvider import androidx.lifecycle.Lifecycle -import net.acumenus.nightingale.ui.PatientScene +import net.acumenus.hummingbird.patient.ui.PatientScene import org.junit.Assert.assertEquals import org.junit.Assert.assertTrue import org.junit.Rule @@ -44,7 +44,7 @@ class PatientPrimaryJourneyInstrumentedTest { composeRule.onNodeWithTag("patient-preferences") .performScrollToNode(hasText("Save preferences")) composeRule.onNodeWithTag("save-patient-preferences").performClick() - composeRule.onNodeWithContentDescription("Back to Nightingale").performClick() + composeRule.onNodeWithContentDescription("Back to Hummingbird").performClick() composeRule.onNodeWithTag("patient-presentation-high-contrast").assertIsDisplayed() composeRule.onNodeWithTag("patient-content") .performScrollToNode(hasText("Your reading preferences")) @@ -60,7 +60,7 @@ class PatientPrimaryJourneyInstrumentedTest { .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) ActivityScenario.launch(intent).use { scenario -> - composeRule.onNodeWithText("Nightingale").assertIsDisplayed() + composeRule.onNodeWithText("Hummingbird Patient").assertIsDisplayed() composeRule.onNodeWithText("Synthetic reference scenario — not a real patient record") .performScrollTo() .assertIsDisplayed() @@ -272,7 +272,7 @@ class PatientPrimaryJourneyInstrumentedTest { "loading" to "Checking your secure patient session", "empty" to "No active hospital stay is available", "unavailable" to "Patient access is temporarily unavailable", - "recoverable-error" to "Nightingale could not connect securely", + "recoverable-error" to "Hummingbird Patient could not connect securely", ) expectedStates.forEach { (preview, expectedText) -> @@ -327,10 +327,10 @@ class PatientPrimaryJourneyInstrumentedTest { composeRule.onNodeWithContentDescription("Manage devices").performClick() composeRule.onNodeWithText("Manage devices").assertIsDisplayed() composeRule.onNodeWithText( - "Manage devices is not available right now. You can keep using Nightingale.", + "Manage devices is not available right now. You can keep using Hummingbird Patient.", ).assertIsDisplayed() - composeRule.onNodeWithContentDescription("Back to Nightingale").performClick() - composeRule.onNodeWithText("Nightingale").assertIsDisplayed() + composeRule.onNodeWithContentDescription("Back to Hummingbird").performClick() + composeRule.onNodeWithText("Hummingbird Patient").assertIsDisplayed() } } } diff --git a/hummingbird/androidPatientApp/app/src/androidTest/java/net/acumenus/nightingale/PatientSecureStorageInstrumentedTest.kt b/hummingbird/androidPatientApp/app/src/androidTest/java/net/acumenus/hummingbird/patient/PatientSecureStorageInstrumentedTest.kt similarity index 87% rename from hummingbird/androidPatientApp/app/src/androidTest/java/net/acumenus/nightingale/PatientSecureStorageInstrumentedTest.kt rename to hummingbird/androidPatientApp/app/src/androidTest/java/net/acumenus/hummingbird/patient/PatientSecureStorageInstrumentedTest.kt index ad087ef5..edafe64c 100644 --- a/hummingbird/androidPatientApp/app/src/androidTest/java/net/acumenus/nightingale/PatientSecureStorageInstrumentedTest.kt +++ b/hummingbird/androidPatientApp/app/src/androidTest/java/net/acumenus/hummingbird/patient/PatientSecureStorageInstrumentedTest.kt @@ -1,9 +1,9 @@ -package net.acumenus.nightingale +package net.acumenus.hummingbird.patient import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.platform.app.InstrumentationRegistry -import net.acumenus.nightingale.data.EncryptedPatientCredentialStore -import net.acumenus.nightingale.data.PatientStoredCredentials +import net.acumenus.hummingbird.patient.data.EncryptedPatientCredentialStore +import net.acumenus.hummingbird.patient.data.PatientStoredCredentials import org.junit.Assert.assertEquals import org.junit.Assert.assertNull import org.junit.Rule diff --git a/hummingbird/androidPatientApp/app/src/androidTest/java/net/acumenus/nightingale/PatientSessionManagementInstrumentedTest.kt b/hummingbird/androidPatientApp/app/src/androidTest/java/net/acumenus/hummingbird/patient/PatientSessionManagementInstrumentedTest.kt similarity index 91% rename from hummingbird/androidPatientApp/app/src/androidTest/java/net/acumenus/nightingale/PatientSessionManagementInstrumentedTest.kt rename to hummingbird/androidPatientApp/app/src/androidTest/java/net/acumenus/hummingbird/patient/PatientSessionManagementInstrumentedTest.kt index 066bf884..b6dfdd56 100644 --- a/hummingbird/androidPatientApp/app/src/androidTest/java/net/acumenus/nightingale/PatientSessionManagementInstrumentedTest.kt +++ b/hummingbird/androidPatientApp/app/src/androidTest/java/net/acumenus/hummingbird/patient/PatientSessionManagementInstrumentedTest.kt @@ -1,4 +1,4 @@ -package net.acumenus.nightingale +package net.acumenus.hummingbird.patient import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.fillMaxSize @@ -19,12 +19,12 @@ import androidx.compose.ui.test.onNodeWithText import androidx.compose.ui.test.performClick import androidx.compose.ui.test.performScrollToNode import androidx.compose.ui.unit.Density -import net.acumenus.nightingale.data.PatientDeviceSession -import net.acumenus.nightingale.data.PatientSessionDevice -import net.acumenus.nightingale.ui.NightingaleTheme -import net.acumenus.nightingale.ui.PatientScene -import net.acumenus.nightingale.ui.PatientScenicBackground -import net.acumenus.nightingale.ui.PatientSessionManagementScreen +import net.acumenus.hummingbird.patient.data.PatientDeviceSession +import net.acumenus.hummingbird.patient.data.PatientSessionDevice +import net.acumenus.hummingbird.patient.ui.HummingbirdPatientTheme +import net.acumenus.hummingbird.patient.ui.PatientScene +import net.acumenus.hummingbird.patient.ui.PatientScenicBackground +import net.acumenus.hummingbird.patient.ui.PatientSessionManagementScreen import org.junit.Assert.assertEquals import org.junit.Assert.assertTrue import org.junit.Rule @@ -42,7 +42,7 @@ class PatientSessionManagementInstrumentedTest { compose.setContent { val density = LocalDensity.current CompositionLocalProvider(LocalDensity provides Density(density.density, fontScale = 2f)) { - NightingaleTheme(darkTheme = true) { + HummingbirdPatientTheme(darkTheme = true) { PatientScenicBackground(scene = PatientScene.PATHWAY) { androidx.compose.material3.Text("Released care pathway details remain readable") } @@ -58,7 +58,7 @@ class PatientSessionManagementInstrumentedTest { fun normalDeviceListMarksCurrentUsesFallbacksAndSelectsExactOtherSession() { var selected: String? = null compose.setContent { - NightingaleTheme(darkTheme = false) { + HummingbirdPatientTheme(darkTheme = false) { PatientSessionManagementScreen( state = PatientDeviceSessionsState.Ready( sessions = listOf( @@ -89,7 +89,7 @@ class PatientSessionManagementInstrumentedTest { fun confirmationWordingDistinguishesOtherAndCurrentDevices() { var confirmed = false compose.setContent { - NightingaleTheme(darkTheme = false) { + HummingbirdPatientTheme(darkTheme = false) { PatientSessionManagementScreen( state = PatientDeviceSessionsState.Ready( sessions = listOf(session(otherUuid, name = "Family tablet")), @@ -115,7 +115,7 @@ class PatientSessionManagementInstrumentedTest { @Test fun currentDeviceConfirmationExplainsImmediateReturnToSignIn() { compose.setContent { - NightingaleTheme(darkTheme = false) { + HummingbirdPatientTheme(darkTheme = false) { val current = session(currentUuid, current = true, name = "This Pixel") PatientSessionManagementScreen( state = PatientDeviceSessionsState.Ready( @@ -133,7 +133,7 @@ class PatientSessionManagementInstrumentedTest { compose.onNodeWithText("Sign out this device?").assertIsDisplayed() compose.onNodeWithText( - "You will return to the Nightingale sign-in screen on this device. Other signed-in devices are not changed.", + "You will return to the Hummingbird Patient sign-in screen on this device. Other signed-in devices are not changed.", ).assertIsDisplayed() } @@ -143,7 +143,7 @@ class PatientSessionManagementInstrumentedTest { compose.setContent { val density = LocalDensity.current CompositionLocalProvider(LocalDensity provides Density(density.density, fontScale = 2f)) { - NightingaleTheme(darkTheme = true) { + HummingbirdPatientTheme(darkTheme = true) { val background = MaterialTheme.colorScheme.background SideEffect { observedBackground = background } Box(Modifier.fillMaxSize()) { @@ -192,7 +192,7 @@ class PatientSessionManagementInstrumentedTest { ), ) compose.setContent { - NightingaleTheme(darkTheme = false) { + HummingbirdPatientTheme(darkTheme = false) { PatientSessionManagementScreen( state = PatientDeviceSessionsState.Ready(sessions = listOf(untrusted)), onDismiss = {}, diff --git a/hummingbird/androidPatientApp/app/src/debug/java/net/acumenus/nightingale/PatientLaunchHooks.kt b/hummingbird/androidPatientApp/app/src/debug/java/net/acumenus/hummingbird/patient/PatientLaunchHooks.kt similarity index 96% rename from hummingbird/androidPatientApp/app/src/debug/java/net/acumenus/nightingale/PatientLaunchHooks.kt rename to hummingbird/androidPatientApp/app/src/debug/java/net/acumenus/hummingbird/patient/PatientLaunchHooks.kt index 66772d48..46f52e13 100644 --- a/hummingbird/androidPatientApp/app/src/debug/java/net/acumenus/nightingale/PatientLaunchHooks.kt +++ b/hummingbird/androidPatientApp/app/src/debug/java/net/acumenus/hummingbird/patient/PatientLaunchHooks.kt @@ -1,4 +1,4 @@ -package net.acumenus.nightingale +package net.acumenus.hummingbird.patient import android.content.Intent diff --git a/hummingbird/androidPatientApp/app/src/debug/java/net/acumenus/nightingale/SyntheticReferencePatientScenario.kt b/hummingbird/androidPatientApp/app/src/debug/java/net/acumenus/hummingbird/patient/SyntheticReferencePatientScenario.kt similarity index 96% rename from hummingbird/androidPatientApp/app/src/debug/java/net/acumenus/nightingale/SyntheticReferencePatientScenario.kt rename to hummingbird/androidPatientApp/app/src/debug/java/net/acumenus/hummingbird/patient/SyntheticReferencePatientScenario.kt index bd59e2f0..a2ab6a10 100644 --- a/hummingbird/androidPatientApp/app/src/debug/java/net/acumenus/nightingale/SyntheticReferencePatientScenario.kt +++ b/hummingbird/androidPatientApp/app/src/debug/java/net/acumenus/hummingbird/patient/SyntheticReferencePatientScenario.kt @@ -1,11 +1,11 @@ -package net.acumenus.nightingale +package net.acumenus.hummingbird.patient -import net.acumenus.nightingale.data.PatientImmediateHelp -import net.acumenus.nightingale.data.PatientMessageThread -import net.acumenus.nightingale.data.PatientMessageThreadTopic -import net.acumenus.nightingale.data.PatientMessageTopic -import net.acumenus.nightingale.data.PatientPreferences -import net.acumenus.nightingale.data.PatientThreadMessage +import net.acumenus.hummingbird.patient.data.PatientImmediateHelp +import net.acumenus.hummingbird.patient.data.PatientMessageThread +import net.acumenus.hummingbird.patient.data.PatientMessageThreadTopic +import net.acumenus.hummingbird.patient.data.PatientMessageTopic +import net.acumenus.hummingbird.patient.data.PatientPreferences +import net.acumenus.hummingbird.patient.data.PatientThreadMessage /** Entirely absent from release: deterministic emulator/UI-test data only. */ internal object SyntheticReferencePatientScenario { @@ -18,7 +18,7 @@ internal object SyntheticReferencePatientScenario { ) PatientLaunchPreview.EMPTY -> PatientSessionState.Empty( patientDisplayName = "Sample inpatient", - message = "No active hospital stay is available in Nightingale.", + message = "No active hospital stay is available in Hummingbird Patient.", ) PatientLaunchPreview.UNAVAILABLE -> PatientSessionState.SignedOut( status = PatientAuthStatus.Unavailable( @@ -27,7 +27,7 @@ internal object SyntheticReferencePatientScenario { ) PatientLaunchPreview.RECOVERABLE_ERROR -> PatientSessionState.SignedOut( status = PatientAuthStatus.Failure( - "Nightingale could not connect securely. Check your connection and try again.", + "Hummingbird Patient could not connect securely. Check your connection and try again.", ), ) } @@ -275,7 +275,7 @@ internal object SyntheticReferencePatientScenario { PatientDestination.MESSAGES to PatientDataContext( heading = "Messages", asOfLabel = "Updated today at 8:42 AM", - sourceLabel = "Source: Nightingale communication service", + sourceLabel = "Source: Hummingbird Patient communication service", uncertaintyNotice = "Messages are for non-urgent questions and are not live emergency chat.", stale = false, ), diff --git a/hummingbird/androidPatientApp/app/src/main/AndroidManifest.xml b/hummingbird/androidPatientApp/app/src/main/AndroidManifest.xml index 0f1124fc..94002db6 100644 --- a/hummingbird/androidPatientApp/app/src/main/AndroidManifest.xml +++ b/hummingbird/androidPatientApp/app/src/main/AndroidManifest.xml @@ -12,12 +12,12 @@ android:networkSecurityConfig="@xml/network_security_config" android:roundIcon="@mipmap/ic_launcher_round" android:supportsRtl="true" - android:theme="@style/Theme.Nightingale" + android:theme="@style/Theme.HummingbirdPatient" android:usesCleartextTraffic="false"> + android:theme="@style/Theme.HummingbirdPatient"> diff --git a/hummingbird/androidPatientApp/app/src/main/java/net/acumenus/nightingale/MainActivity.kt b/hummingbird/androidPatientApp/app/src/main/java/net/acumenus/hummingbird/patient/MainActivity.kt similarity index 82% rename from hummingbird/androidPatientApp/app/src/main/java/net/acumenus/nightingale/MainActivity.kt rename to hummingbird/androidPatientApp/app/src/main/java/net/acumenus/hummingbird/patient/MainActivity.kt index ec02dd92..894172c0 100644 --- a/hummingbird/androidPatientApp/app/src/main/java/net/acumenus/nightingale/MainActivity.kt +++ b/hummingbird/androidPatientApp/app/src/main/java/net/acumenus/hummingbird/patient/MainActivity.kt @@ -1,4 +1,4 @@ -package net.acumenus.nightingale +package net.acumenus.hummingbird.patient import android.os.Bundle import android.os.Build @@ -11,14 +11,14 @@ import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.setValue -import net.acumenus.nightingale.ui.NightingaleTheme -import net.acumenus.nightingale.ui.PatientApp -import net.acumenus.nightingale.ui.PatientPresentationAccessibilityProvider -import net.acumenus.nightingale.data.EncryptedPatientCredentialStore -import net.acumenus.nightingale.data.PatientApiClient -import net.acumenus.nightingale.data.PatientApiConfiguration -import net.acumenus.nightingale.data.PatientDeviceDescriptor -import net.acumenus.nightingale.data.PatientSessionCoordinator +import net.acumenus.hummingbird.patient.ui.HummingbirdPatientTheme +import net.acumenus.hummingbird.patient.ui.PatientApp +import net.acumenus.hummingbird.patient.ui.PatientPresentationAccessibilityProvider +import net.acumenus.hummingbird.patient.data.EncryptedPatientCredentialStore +import net.acumenus.hummingbird.patient.data.PatientApiClient +import net.acumenus.hummingbird.patient.data.PatientApiConfiguration +import net.acumenus.hummingbird.patient.data.PatientDeviceDescriptor +import net.acumenus.hummingbird.patient.data.PatientSessionCoordinator class MainActivity : ComponentActivity() { private var privacyCovered by mutableStateOf(false) @@ -66,7 +66,7 @@ class MainActivity : ComponentActivity() { val presentationPreferences = (viewModel.state.session as? PatientSessionState.Ready) ?.snapshot ?.preferences - NightingaleTheme( + HummingbirdPatientTheme( highContrast = presentationPreferences?.highContrast == true, ) { PatientPresentationAccessibilityProvider(preferences = presentationPreferences) { diff --git a/hummingbird/androidPatientApp/app/src/main/java/net/acumenus/nightingale/PatientAppViewModel.kt b/hummingbird/androidPatientApp/app/src/main/java/net/acumenus/hummingbird/patient/PatientAppViewModel.kt similarity index 97% rename from hummingbird/androidPatientApp/app/src/main/java/net/acumenus/nightingale/PatientAppViewModel.kt rename to hummingbird/androidPatientApp/app/src/main/java/net/acumenus/hummingbird/patient/PatientAppViewModel.kt index 81b97c3c..7bf36866 100644 --- a/hummingbird/androidPatientApp/app/src/main/java/net/acumenus/nightingale/PatientAppViewModel.kt +++ b/hummingbird/androidPatientApp/app/src/main/java/net/acumenus/hummingbird/patient/PatientAppViewModel.kt @@ -1,4 +1,4 @@ -package net.acumenus.nightingale +package net.acumenus.hummingbird.patient import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf @@ -12,13 +12,13 @@ import kotlinx.coroutines.SupervisorJob import kotlinx.coroutines.cancel import kotlinx.coroutines.launch import kotlinx.coroutines.withContext -import net.acumenus.nightingale.data.PatientApiConfiguration -import net.acumenus.nightingale.data.PatientApiException -import net.acumenus.nightingale.data.PatientEnrollmentRequest -import net.acumenus.nightingale.data.PatientMessageAmendmentAction -import net.acumenus.nightingale.data.PatientPreferencesUpdate -import net.acumenus.nightingale.data.PatientSessionCoordinator -import net.acumenus.nightingale.data.PatientSessionOutcome +import net.acumenus.hummingbird.patient.data.PatientApiConfiguration +import net.acumenus.hummingbird.patient.data.PatientApiException +import net.acumenus.hummingbird.patient.data.PatientEnrollmentRequest +import net.acumenus.hummingbird.patient.data.PatientMessageAmendmentAction +import net.acumenus.hummingbird.patient.data.PatientPreferencesUpdate +import net.acumenus.hummingbird.patient.data.PatientSessionCoordinator +import net.acumenus.hummingbird.patient.data.PatientSessionOutcome internal class PatientAppViewModel( private val apiEnabled: Boolean, @@ -203,7 +203,7 @@ internal class PatientAppViewModel( ready.synthetic || !apiEnabled || coordinator == null -> { state = state.copy( deviceSessions = PatientDeviceSessionsState.Unavailable( - message = "Manage devices is not available right now. You can keep using Nightingale.", + message = "Manage devices is not available right now. You can keep using Hummingbird Patient.", canRetry = false, ), ) @@ -408,7 +408,7 @@ internal class PatientAppViewModel( } else if (error.statusCode == 404) { "Device management is not available right now. No additional request was sent." } else { - "Nightingale could not confirm that device was signed out. Review the list before trying again." + "Hummingbird could not confirm that device was signed out. Review the list before trying again." }, ) } @@ -417,7 +417,7 @@ internal class PatientAppViewModel( if (revocationConfirmed) { "That device was signed out, but the device list could not be refreshed. Refresh the list to review current sessions." } else { - "Nightingale could not confirm that device was signed out. Review the list before trying again." + "Hummingbird could not confirm that device was signed out. Review the list before trying again." }, ) } finally { @@ -826,7 +826,7 @@ internal class PatientAppViewModel( state = state.copy( deviceSessions = PatientDeviceSessionsState.Unavailable( message = if (featureDisabled) { - "Manage devices is not available right now. You can keep using Nightingale." + "Manage devices is not available right now. You can keep using Hummingbird Patient." } else { "Your devices could not be checked right now. Your care information is still available." }, @@ -992,7 +992,7 @@ internal class PatientAppViewModel( updateMessaging { current -> current.copy( operation = PatientMessagingOperation.Failure( - "Nightingale could not confirm this request. Refresh before trying again; use your call button for immediate help.", + "Hummingbird could not confirm this request. Refresh before trying again; use your call button for immediate help.", ), ) } @@ -1013,9 +1013,9 @@ internal class PatientAppViewModel( else -> null } - private fun List.replaceSummary( - updated: net.acumenus.nightingale.data.PatientMessageThread, - ): List = + private fun List.replaceSummary( + updated: net.acumenus.hummingbird.patient.data.PatientMessageThread, + ): List = map { existing -> if (existing.threadUuid == updated.threadUuid) updated else existing } private fun Exception.patientMessage(): String = when (this) { @@ -1024,9 +1024,9 @@ internal class PatientAppViewModel( 403, 404 -> "That patient information is not available. Ask your care team if you expected to see it." 422 -> message ?: "The invitation or account information could not be verified." 429 -> "Too many attempts. Wait a moment before trying again." - else -> "Nightingale could not load your information securely. Try again." + else -> "Hummingbird Patient could not load your information securely. Try again." } - else -> "Nightingale could not connect securely. Check your connection and try again." + else -> "Hummingbird Patient could not connect securely. Check your connection and try again." } private companion object { diff --git a/hummingbird/androidPatientApp/app/src/main/java/net/acumenus/nightingale/PatientExperienceModels.kt b/hummingbird/androidPatientApp/app/src/main/java/net/acumenus/hummingbird/patient/PatientExperienceModels.kt similarity index 95% rename from hummingbird/androidPatientApp/app/src/main/java/net/acumenus/nightingale/PatientExperienceModels.kt rename to hummingbird/androidPatientApp/app/src/main/java/net/acumenus/hummingbird/patient/PatientExperienceModels.kt index 60126401..4ca57fed 100644 --- a/hummingbird/androidPatientApp/app/src/main/java/net/acumenus/nightingale/PatientExperienceModels.kt +++ b/hummingbird/androidPatientApp/app/src/main/java/net/acumenus/hummingbird/patient/PatientExperienceModels.kt @@ -1,11 +1,11 @@ -package net.acumenus.nightingale - -import net.acumenus.nightingale.data.PatientImmediateHelp -import net.acumenus.nightingale.data.PatientDeviceSession -import net.acumenus.nightingale.data.PatientMessageThread -import net.acumenus.nightingale.data.PatientMessageTopic -import net.acumenus.nightingale.data.PatientPreferences -import net.acumenus.nightingale.data.PatientPreferencesUpdate +package net.acumenus.hummingbird.patient + +import net.acumenus.hummingbird.patient.data.PatientImmediateHelp +import net.acumenus.hummingbird.patient.data.PatientDeviceSession +import net.acumenus.hummingbird.patient.data.PatientMessageThread +import net.acumenus.hummingbird.patient.data.PatientMessageTopic +import net.acumenus.hummingbird.patient.data.PatientPreferences +import net.acumenus.hummingbird.patient.data.PatientPreferencesUpdate enum class PatientDestination(val label: String) { TODAY("Today"), diff --git a/hummingbird/androidPatientApp/app/src/main/java/net/acumenus/nightingale/PatientLaunchState.kt b/hummingbird/androidPatientApp/app/src/main/java/net/acumenus/hummingbird/patient/PatientLaunchState.kt similarity index 92% rename from hummingbird/androidPatientApp/app/src/main/java/net/acumenus/nightingale/PatientLaunchState.kt rename to hummingbird/androidPatientApp/app/src/main/java/net/acumenus/hummingbird/patient/PatientLaunchState.kt index 7149cde3..4a60c092 100644 --- a/hummingbird/androidPatientApp/app/src/main/java/net/acumenus/nightingale/PatientLaunchState.kt +++ b/hummingbird/androidPatientApp/app/src/main/java/net/acumenus/hummingbird/patient/PatientLaunchState.kt @@ -1,4 +1,4 @@ -package net.acumenus.nightingale +package net.acumenus.hummingbird.patient /** Debug may request a synthetic scenario; release always returns this default. */ internal data class PatientLaunchState( diff --git a/hummingbird/androidPatientApp/app/src/main/java/net/acumenus/nightingale/PatientPrivacyPolicy.kt b/hummingbird/androidPatientApp/app/src/main/java/net/acumenus/hummingbird/patient/PatientPrivacyPolicy.kt similarity index 86% rename from hummingbird/androidPatientApp/app/src/main/java/net/acumenus/nightingale/PatientPrivacyPolicy.kt rename to hummingbird/androidPatientApp/app/src/main/java/net/acumenus/hummingbird/patient/PatientPrivacyPolicy.kt index 46247bc1..e4ea9d8b 100644 --- a/hummingbird/androidPatientApp/app/src/main/java/net/acumenus/nightingale/PatientPrivacyPolicy.kt +++ b/hummingbird/androidPatientApp/app/src/main/java/net/acumenus/hummingbird/patient/PatientPrivacyPolicy.kt @@ -1,4 +1,4 @@ -package net.acumenus.nightingale +package net.acumenus.hummingbird.patient import android.view.Window import android.view.WindowManager diff --git a/hummingbird/androidPatientApp/app/src/main/java/net/acumenus/nightingale/data/PatientApiClient.kt b/hummingbird/androidPatientApp/app/src/main/java/net/acumenus/hummingbird/patient/data/PatientApiClient.kt similarity index 99% rename from hummingbird/androidPatientApp/app/src/main/java/net/acumenus/nightingale/data/PatientApiClient.kt rename to hummingbird/androidPatientApp/app/src/main/java/net/acumenus/hummingbird/patient/data/PatientApiClient.kt index 8a2b3ab5..660f4011 100644 --- a/hummingbird/androidPatientApp/app/src/main/java/net/acumenus/nightingale/data/PatientApiClient.kt +++ b/hummingbird/androidPatientApp/app/src/main/java/net/acumenus/hummingbird/patient/data/PatientApiClient.kt @@ -1,6 +1,6 @@ -package net.acumenus.nightingale.data +package net.acumenus.hummingbird.patient.data -import net.acumenus.nightingale.BuildConfig +import net.acumenus.hummingbird.patient.BuildConfig import okhttp3.CacheControl import okhttp3.MediaType.Companion.toMediaType import okhttp3.OkHttpClient diff --git a/hummingbird/androidPatientApp/app/src/main/java/net/acumenus/nightingale/data/PatientApiModels.kt b/hummingbird/androidPatientApp/app/src/main/java/net/acumenus/hummingbird/patient/data/PatientApiModels.kt similarity index 99% rename from hummingbird/androidPatientApp/app/src/main/java/net/acumenus/nightingale/data/PatientApiModels.kt rename to hummingbird/androidPatientApp/app/src/main/java/net/acumenus/hummingbird/patient/data/PatientApiModels.kt index f78ab346..a214edfb 100644 --- a/hummingbird/androidPatientApp/app/src/main/java/net/acumenus/nightingale/data/PatientApiModels.kt +++ b/hummingbird/androidPatientApp/app/src/main/java/net/acumenus/hummingbird/patient/data/PatientApiModels.kt @@ -1,4 +1,4 @@ -package net.acumenus.nightingale.data +package net.acumenus.hummingbird.patient.data import org.json.JSONArray import org.json.JSONObject diff --git a/hummingbird/androidPatientApp/app/src/main/java/net/acumenus/nightingale/data/PatientSecureStore.kt b/hummingbird/androidPatientApp/app/src/main/java/net/acumenus/hummingbird/patient/data/PatientSecureStore.kt similarity index 98% rename from hummingbird/androidPatientApp/app/src/main/java/net/acumenus/nightingale/data/PatientSecureStore.kt rename to hummingbird/androidPatientApp/app/src/main/java/net/acumenus/hummingbird/patient/data/PatientSecureStore.kt index aace5119..a1f28e29 100644 --- a/hummingbird/androidPatientApp/app/src/main/java/net/acumenus/nightingale/data/PatientSecureStore.kt +++ b/hummingbird/androidPatientApp/app/src/main/java/net/acumenus/hummingbird/patient/data/PatientSecureStore.kt @@ -1,4 +1,4 @@ -package net.acumenus.nightingale.data +package net.acumenus.hummingbird.patient.data import android.content.Context import android.content.SharedPreferences @@ -6,7 +6,7 @@ import androidx.security.crypto.EncryptedSharedPreferences import androidx.security.crypto.MasterKey import java.util.UUID -/** Patient credentials never share a file, key alias, or migration with staff Nightingale. */ +/** Patient credentials never share a file, key alias, or migration with staff Hummingbird. */ object PatientStoragePolicy { internal const val PREFERENCES_FILE = "hummingbird_patient_secure_v1" internal const val MASTER_KEY_ALIAS = "hummingbird_patient_master_key_v1" diff --git a/hummingbird/androidPatientApp/app/src/main/java/net/acumenus/nightingale/data/PatientSessionCoordinator.kt b/hummingbird/androidPatientApp/app/src/main/java/net/acumenus/hummingbird/patient/data/PatientSessionCoordinator.kt similarity index 95% rename from hummingbird/androidPatientApp/app/src/main/java/net/acumenus/nightingale/data/PatientSessionCoordinator.kt rename to hummingbird/androidPatientApp/app/src/main/java/net/acumenus/hummingbird/patient/data/PatientSessionCoordinator.kt index 4f851885..a5258d0d 100644 --- a/hummingbird/androidPatientApp/app/src/main/java/net/acumenus/nightingale/data/PatientSessionCoordinator.kt +++ b/hummingbird/androidPatientApp/app/src/main/java/net/acumenus/hummingbird/patient/data/PatientSessionCoordinator.kt @@ -1,23 +1,23 @@ -package net.acumenus.nightingale.data - -import net.acumenus.nightingale.PatientCareTeamMember -import net.acumenus.nightingale.PatientDataContext -import net.acumenus.nightingale.PatientDestination -import net.acumenus.nightingale.PatientDischargeReadinessContact -import net.acumenus.nightingale.PatientDischargeReadinessCriterion -import net.acumenus.nightingale.PatientDischargeReadinessFollowUp -import net.acumenus.nightingale.PatientDischargeReadinessMedication -import net.acumenus.nightingale.PatientDischargeReadinessView -import net.acumenus.nightingale.PatientEducation -import net.acumenus.nightingale.PatientGoal -import net.acumenus.nightingale.PatientMilestone -import net.acumenus.nightingale.PatientPathwayEventView -import net.acumenus.nightingale.PatientPathwayEventsView -import net.acumenus.nightingale.PatientPathStep -import net.acumenus.nightingale.PatientRoundsSummaryView -import net.acumenus.nightingale.PatientRoundsTopicView -import net.acumenus.nightingale.PatientSnapshot -import net.acumenus.nightingale.PatientTodayItem +package net.acumenus.hummingbird.patient.data + +import net.acumenus.hummingbird.patient.PatientCareTeamMember +import net.acumenus.hummingbird.patient.PatientDataContext +import net.acumenus.hummingbird.patient.PatientDestination +import net.acumenus.hummingbird.patient.PatientDischargeReadinessContact +import net.acumenus.hummingbird.patient.PatientDischargeReadinessCriterion +import net.acumenus.hummingbird.patient.PatientDischargeReadinessFollowUp +import net.acumenus.hummingbird.patient.PatientDischargeReadinessMedication +import net.acumenus.hummingbird.patient.PatientDischargeReadinessView +import net.acumenus.hummingbird.patient.PatientEducation +import net.acumenus.hummingbird.patient.PatientGoal +import net.acumenus.hummingbird.patient.PatientMilestone +import net.acumenus.hummingbird.patient.PatientPathwayEventView +import net.acumenus.hummingbird.patient.PatientPathwayEventsView +import net.acumenus.hummingbird.patient.PatientPathStep +import net.acumenus.hummingbird.patient.PatientRoundsSummaryView +import net.acumenus.hummingbird.patient.PatientRoundsTopicView +import net.acumenus.hummingbird.patient.PatientSnapshot +import net.acumenus.hummingbird.patient.PatientTodayItem import java.time.OffsetDateTime import java.time.format.DateTimeFormatter import java.util.UUID @@ -252,7 +252,7 @@ class PatientSessionCoordinator( val encounter = api.encounters(token).data.encounters.firstOrNull() ?: return PatientSessionOutcome.Empty( displayName = profile.displayName, - message = "No active hospital stay is available in Nightingale. Ask your care team if you expected to see one.", + message = "No active hospital stay is available in Hummingbird Patient. Ask your care team if you expected to see one.", ) val projections = PatientProjectionBundle( diff --git a/hummingbird/androidPatientApp/app/src/main/java/net/acumenus/nightingale/data/PatientStateVocabulary.kt b/hummingbird/androidPatientApp/app/src/main/java/net/acumenus/hummingbird/patient/data/PatientStateVocabulary.kt similarity index 98% rename from hummingbird/androidPatientApp/app/src/main/java/net/acumenus/nightingale/data/PatientStateVocabulary.kt rename to hummingbird/androidPatientApp/app/src/main/java/net/acumenus/hummingbird/patient/data/PatientStateVocabulary.kt index f20dc824..c297a0b7 100644 --- a/hummingbird/androidPatientApp/app/src/main/java/net/acumenus/nightingale/data/PatientStateVocabulary.kt +++ b/hummingbird/androidPatientApp/app/src/main/java/net/acumenus/hummingbird/patient/data/PatientStateVocabulary.kt @@ -1,4 +1,4 @@ -package net.acumenus.nightingale.data +package net.acumenus.hummingbird.patient.data /** * The patient API transmits stable state codes. Keep their patient-language diff --git a/hummingbird/androidPatientApp/app/src/main/java/net/acumenus/nightingale/ui/NightingaleTheme.kt b/hummingbird/androidPatientApp/app/src/main/java/net/acumenus/hummingbird/patient/ui/HummingbirdPatientTheme.kt similarity index 97% rename from hummingbird/androidPatientApp/app/src/main/java/net/acumenus/nightingale/ui/NightingaleTheme.kt rename to hummingbird/androidPatientApp/app/src/main/java/net/acumenus/hummingbird/patient/ui/HummingbirdPatientTheme.kt index 2bb528ea..5cb2295e 100644 --- a/hummingbird/androidPatientApp/app/src/main/java/net/acumenus/nightingale/ui/NightingaleTheme.kt +++ b/hummingbird/androidPatientApp/app/src/main/java/net/acumenus/hummingbird/patient/ui/HummingbirdPatientTheme.kt @@ -1,4 +1,4 @@ -package net.acumenus.nightingale.ui +package net.acumenus.hummingbird.patient.ui import androidx.compose.foundation.isSystemInDarkTheme import androidx.compose.material3.MaterialTheme @@ -93,7 +93,7 @@ private val PatientHighContrastDarkColors = darkColorScheme( ) @Composable -fun NightingaleTheme( +fun HummingbirdPatientTheme( darkTheme: Boolean = isSystemInDarkTheme(), highContrast: Boolean = false, content: @Composable () -> Unit, diff --git a/hummingbird/androidPatientApp/app/src/main/java/net/acumenus/nightingale/ui/PatientApp.kt b/hummingbird/androidPatientApp/app/src/main/java/net/acumenus/hummingbird/patient/ui/PatientApp.kt similarity index 93% rename from hummingbird/androidPatientApp/app/src/main/java/net/acumenus/nightingale/ui/PatientApp.kt rename to hummingbird/androidPatientApp/app/src/main/java/net/acumenus/hummingbird/patient/ui/PatientApp.kt index b323408c..af56bc3b 100644 --- a/hummingbird/androidPatientApp/app/src/main/java/net/acumenus/nightingale/ui/PatientApp.kt +++ b/hummingbird/androidPatientApp/app/src/main/java/net/acumenus/hummingbird/patient/ui/PatientApp.kt @@ -1,4 +1,4 @@ -package net.acumenus.nightingale.ui +package net.acumenus.hummingbird.patient.ui import androidx.compose.foundation.background import androidx.compose.foundation.layout.Box @@ -23,11 +23,11 @@ import androidx.compose.ui.semantics.liveRegion import androidx.compose.ui.semantics.semantics import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.unit.dp -import net.acumenus.nightingale.PatientAppViewModel -import net.acumenus.nightingale.PatientDeviceSessionsState -import net.acumenus.nightingale.PatientPreferencesState -import net.acumenus.nightingale.PatientSessionState -import net.acumenus.nightingale.SyntheticReferencePatientScenario +import net.acumenus.hummingbird.patient.PatientAppViewModel +import net.acumenus.hummingbird.patient.PatientDeviceSessionsState +import net.acumenus.hummingbird.patient.PatientPreferencesState +import net.acumenus.hummingbird.patient.PatientSessionState +import net.acumenus.hummingbird.patient.SyntheticReferencePatientScenario @Composable internal fun PatientApp( @@ -112,12 +112,12 @@ internal fun PatientApp( .fillMaxSize() .background(MaterialTheme.colorScheme.surface) .clearAndSetSemantics { - contentDescription = "Nightingale is hidden for privacy" + contentDescription = "Hummingbird Patient is hidden for privacy" }, contentAlignment = Alignment.Center, ) { Text( - text = "Nightingale", + text = "Hummingbird Patient", style = MaterialTheme.typography.titleLarge, ) } diff --git a/hummingbird/androidPatientApp/app/src/main/java/net/acumenus/nightingale/ui/PatientAuthenticationScreen.kt b/hummingbird/androidPatientApp/app/src/main/java/net/acumenus/hummingbird/patient/ui/PatientAuthenticationScreen.kt similarity index 97% rename from hummingbird/androidPatientApp/app/src/main/java/net/acumenus/nightingale/ui/PatientAuthenticationScreen.kt rename to hummingbird/androidPatientApp/app/src/main/java/net/acumenus/hummingbird/patient/ui/PatientAuthenticationScreen.kt index df139310..ca5fc843 100644 --- a/hummingbird/androidPatientApp/app/src/main/java/net/acumenus/nightingale/ui/PatientAuthenticationScreen.kt +++ b/hummingbird/androidPatientApp/app/src/main/java/net/acumenus/hummingbird/patient/ui/PatientAuthenticationScreen.kt @@ -1,4 +1,4 @@ -package net.acumenus.nightingale.ui +package net.acumenus.hummingbird.patient.ui import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column @@ -42,10 +42,10 @@ import androidx.compose.ui.text.input.ImeAction import androidx.compose.ui.text.input.KeyboardType import androidx.compose.ui.text.input.PasswordVisualTransformation import androidx.compose.ui.unit.dp -import net.acumenus.nightingale.PatientAuthMode -import net.acumenus.nightingale.PatientAuthStatus -import net.acumenus.nightingale.PatientEnrollmentForm -import net.acumenus.nightingale.PatientSessionState +import net.acumenus.hummingbird.patient.PatientAuthMode +import net.acumenus.hummingbird.patient.PatientAuthStatus +import net.acumenus.hummingbird.patient.PatientEnrollmentForm +import net.acumenus.hummingbird.patient.PatientSessionState @Composable internal fun PatientAuthenticationScreen( @@ -71,7 +71,7 @@ internal fun PatientAuthenticationScreen( tint = MaterialTheme.colorScheme.primary, ) Text( - text = "Nightingale", + text = "Hummingbird Patient", style = MaterialTheme.typography.headlineMedium, fontWeight = FontWeight.Bold, modifier = Modifier.semantics { heading() }, diff --git a/hummingbird/androidPatientApp/app/src/main/java/net/acumenus/nightingale/ui/PatientExperienceScreen.kt b/hummingbird/androidPatientApp/app/src/main/java/net/acumenus/hummingbird/patient/ui/PatientExperienceScreen.kt similarity index 96% rename from hummingbird/androidPatientApp/app/src/main/java/net/acumenus/nightingale/ui/PatientExperienceScreen.kt rename to hummingbird/androidPatientApp/app/src/main/java/net/acumenus/hummingbird/patient/ui/PatientExperienceScreen.kt index 41402562..a1728d27 100644 --- a/hummingbird/androidPatientApp/app/src/main/java/net/acumenus/nightingale/ui/PatientExperienceScreen.kt +++ b/hummingbird/androidPatientApp/app/src/main/java/net/acumenus/hummingbird/patient/ui/PatientExperienceScreen.kt @@ -1,4 +1,4 @@ -package net.acumenus.nightingale.ui +package net.acumenus.hummingbird.patient.ui import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column @@ -50,14 +50,14 @@ import androidx.compose.ui.platform.testTag import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.unit.dp -import net.acumenus.nightingale.PatientCareTeamMember -import net.acumenus.nightingale.PatientDataContext -import net.acumenus.nightingale.PatientDestination -import net.acumenus.nightingale.PatientEducation -import net.acumenus.nightingale.PatientMessagingState -import net.acumenus.nightingale.PatientPathStep -import net.acumenus.nightingale.PatientSnapshot -import net.acumenus.nightingale.PatientTodayItem +import net.acumenus.hummingbird.patient.PatientCareTeamMember +import net.acumenus.hummingbird.patient.PatientDataContext +import net.acumenus.hummingbird.patient.PatientDestination +import net.acumenus.hummingbird.patient.PatientEducation +import net.acumenus.hummingbird.patient.PatientMessagingState +import net.acumenus.hummingbird.patient.PatientPathStep +import net.acumenus.hummingbird.patient.PatientSnapshot +import net.acumenus.hummingbird.patient.PatientTodayItem @OptIn(ExperimentalMaterial3Api::class) @Composable @@ -73,7 +73,7 @@ internal fun PatientExperienceScreen( onCreateMessageThread: (String, String) -> Unit, onRequestEducationClarification: (String, String) -> Unit, onSendMessage: (String) -> Unit, - onAmendMessage: (String, net.acumenus.nightingale.data.PatientMessageAmendmentAction, String?) -> Unit, + onAmendMessage: (String, net.acumenus.hummingbird.patient.data.PatientMessageAmendmentAction, String?) -> Unit, onCloseMessageThread: () -> Unit, onManagePreferences: () -> Unit, onManageDevices: () -> Unit, @@ -99,7 +99,7 @@ internal fun PatientExperienceScreen( "No released update is available" }, sourceLabel = if (selectedDestination == PatientDestination.MESSAGES) { - "Source: Nightingale communication service" + "Source: Hummingbird Patient communication service" } else { "Source: no patient-facing projection released" }, @@ -117,7 +117,7 @@ internal fun PatientExperienceScreen( TopAppBar( title = { Text( - text = "Nightingale", + text = "Hummingbird Patient", style = MaterialTheme.typography.titleMedium, maxLines = 1, overflow = TextOverflow.Ellipsis, @@ -243,7 +243,7 @@ private fun PatientPresentationPreferenceNotice(modifier: Modifier = Modifier) { GuidanceCard( title = "Your reading preferences", - body = "Nightingale is using ${choices.joinToString()}. Your device accessibility settings can make text larger. These display choices do not change your care plan, clinical orders, or urgent-help instructions.", + body = "Hummingbird Patient is using ${choices.joinToString()}. Your device accessibility settings can make text larger. These display choices do not change your care plan, clinical orders, or urgent-help instructions.", modifier = modifier.testTag("patient-presentation-preference-notice"), ) } @@ -263,7 +263,7 @@ private fun androidx.compose.foundation.lazy.LazyListScope.todayContent(snapshot item { GuidanceCard( title = "Today’s plan is not available yet", - body = "Ask your care team for the current plan. Nightingale only shows information that has been released for you.", + body = "Ask your care team for the current plan. Hummingbird Patient only shows information that has been released for you.", modifier = Modifier.padding(horizontal = 16.dp), ) } @@ -618,7 +618,7 @@ private fun androidx.compose.foundation.lazy.LazyListScope.messagesContent( onLeaveMessageThread: () -> Unit, onCreateMessageThread: (String, String) -> Unit, onSendMessage: (String) -> Unit, - onAmendMessage: (String, net.acumenus.nightingale.data.PatientMessageAmendmentAction, String?) -> Unit, + onAmendMessage: (String, net.acumenus.hummingbird.patient.data.PatientMessageAmendmentAction, String?) -> Unit, onCloseMessageThread: () -> Unit, ) { item { diff --git a/hummingbird/androidPatientApp/app/src/main/java/net/acumenus/nightingale/ui/PatientMessagingPanel.kt b/hummingbird/androidPatientApp/app/src/main/java/net/acumenus/hummingbird/patient/ui/PatientMessagingPanel.kt similarity index 97% rename from hummingbird/androidPatientApp/app/src/main/java/net/acumenus/nightingale/ui/PatientMessagingPanel.kt rename to hummingbird/androidPatientApp/app/src/main/java/net/acumenus/hummingbird/patient/ui/PatientMessagingPanel.kt index 5150cbe0..c75bf605 100644 --- a/hummingbird/androidPatientApp/app/src/main/java/net/acumenus/nightingale/ui/PatientMessagingPanel.kt +++ b/hummingbird/androidPatientApp/app/src/main/java/net/acumenus/hummingbird/patient/ui/PatientMessagingPanel.kt @@ -1,4 +1,4 @@ -package net.acumenus.nightingale.ui +package net.acumenus.hummingbird.patient.ui import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column @@ -32,11 +32,11 @@ import androidx.compose.ui.semantics.semantics import androidx.compose.ui.platform.testTag import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.unit.dp -import net.acumenus.nightingale.PatientMessagingOperation -import net.acumenus.nightingale.PatientMessagingState -import net.acumenus.nightingale.data.PatientMessageAmendmentAction -import net.acumenus.nightingale.data.PatientMessageThread -import net.acumenus.nightingale.data.PatientMessageTopic +import net.acumenus.hummingbird.patient.PatientMessagingOperation +import net.acumenus.hummingbird.patient.PatientMessagingState +import net.acumenus.hummingbird.patient.data.PatientMessageAmendmentAction +import net.acumenus.hummingbird.patient.data.PatientMessageThread +import net.acumenus.hummingbird.patient.data.PatientMessageTopic @Composable internal fun PatientMessagingPanel( @@ -316,9 +316,9 @@ private fun ThreadConversation( ) { var reply by remember(thread.threadUuid) { mutableStateOf("") } var confirmingClose by remember(thread.threadUuid) { mutableStateOf(false) } - var correctionTarget by remember(thread.threadUuid) { mutableStateOf(null) } + var correctionTarget by remember(thread.threadUuid) { mutableStateOf(null) } var correctionDraft by remember(thread.threadUuid) { mutableStateOf("") } - var withdrawalTarget by remember(thread.threadUuid) { mutableStateOf(null) } + var withdrawalTarget by remember(thread.threadUuid) { mutableStateOf(null) } val isUnsharedRoundsQuestion = thread.topic.code == "rounds_question" && thread.messages.none { it.messageKind == "system_status" } diff --git a/hummingbird/androidPatientApp/app/src/main/java/net/acumenus/nightingale/ui/PatientPreferencesScreen.kt b/hummingbird/androidPatientApp/app/src/main/java/net/acumenus/hummingbird/patient/ui/PatientPreferencesScreen.kt similarity index 96% rename from hummingbird/androidPatientApp/app/src/main/java/net/acumenus/nightingale/ui/PatientPreferencesScreen.kt rename to hummingbird/androidPatientApp/app/src/main/java/net/acumenus/hummingbird/patient/ui/PatientPreferencesScreen.kt index 1c486dae..9d5268a1 100644 --- a/hummingbird/androidPatientApp/app/src/main/java/net/acumenus/nightingale/ui/PatientPreferencesScreen.kt +++ b/hummingbird/androidPatientApp/app/src/main/java/net/acumenus/hummingbird/patient/ui/PatientPreferencesScreen.kt @@ -1,4 +1,4 @@ -package net.acumenus.nightingale.ui +package net.acumenus.hummingbird.patient.ui import androidx.activity.compose.BackHandler import androidx.compose.foundation.layout.Arrangement @@ -34,8 +34,8 @@ import androidx.compose.ui.semantics.heading import androidx.compose.ui.semantics.semantics import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.unit.dp -import net.acumenus.nightingale.PatientPreferencesState -import net.acumenus.nightingale.data.PatientPreferencesUpdate +import net.acumenus.hummingbird.patient.PatientPreferencesState +import net.acumenus.hummingbird.patient.data.PatientPreferencesUpdate @OptIn(ExperimentalMaterial3Api::class) @Composable @@ -55,7 +55,7 @@ internal fun PatientPreferencesScreen( IconButton(onClick = onDismiss) { Icon( Icons.AutoMirrored.Outlined.ArrowBack, - contentDescription = "Back to Nightingale", + contentDescription = "Back to Hummingbird", ) } }, @@ -127,7 +127,7 @@ private fun PatientPreferencesForm( ) { item { Text( - text = "Make Nightingale feel right for you", + text = "Make Hummingbird feel right for you", style = MaterialTheme.typography.headlineSmall, fontWeight = FontWeight.Bold, modifier = Modifier.semantics { heading() }, @@ -167,7 +167,7 @@ private fun PatientPreferencesForm( testTag = "patient-preference-high-contrast", ) Text( - "Nightingale also respects the accessibility settings on this device.", + "Hummingbird also respects the accessibility settings on this device.", style = MaterialTheme.typography.bodySmall, color = MaterialTheme.colorScheme.onSurfaceVariant, ) diff --git a/hummingbird/androidPatientApp/app/src/main/java/net/acumenus/nightingale/ui/PatientPresentationAccessibility.kt b/hummingbird/androidPatientApp/app/src/main/java/net/acumenus/hummingbird/patient/ui/PatientPresentationAccessibility.kt similarity index 95% rename from hummingbird/androidPatientApp/app/src/main/java/net/acumenus/nightingale/ui/PatientPresentationAccessibility.kt rename to hummingbird/androidPatientApp/app/src/main/java/net/acumenus/hummingbird/patient/ui/PatientPresentationAccessibility.kt index 3bdadad2..4e4bc046 100644 --- a/hummingbird/androidPatientApp/app/src/main/java/net/acumenus/nightingale/ui/PatientPresentationAccessibility.kt +++ b/hummingbird/androidPatientApp/app/src/main/java/net/acumenus/hummingbird/patient/ui/PatientPresentationAccessibility.kt @@ -1,4 +1,4 @@ -package net.acumenus.nightingale.ui +package net.acumenus.hummingbird.patient.ui import androidx.compose.runtime.Composable import androidx.compose.runtime.CompositionLocalProvider @@ -7,7 +7,7 @@ import androidx.compose.runtime.staticCompositionLocalOf import androidx.compose.ui.platform.LocalDensity import androidx.compose.ui.unit.Density import kotlin.math.max -import net.acumenus.nightingale.data.PatientPreferences +import net.acumenus.hummingbird.patient.data.PatientPreferences /** * Rendering-only account choices. They never alter clinical data, urgency diff --git a/hummingbird/androidPatientApp/app/src/main/java/net/acumenus/nightingale/ui/PatientScenicBackground.kt b/hummingbird/androidPatientApp/app/src/main/java/net/acumenus/hummingbird/patient/ui/PatientScenicBackground.kt similarity index 95% rename from hummingbird/androidPatientApp/app/src/main/java/net/acumenus/nightingale/ui/PatientScenicBackground.kt rename to hummingbird/androidPatientApp/app/src/main/java/net/acumenus/hummingbird/patient/ui/PatientScenicBackground.kt index 701df801..16459e7b 100644 --- a/hummingbird/androidPatientApp/app/src/main/java/net/acumenus/nightingale/ui/PatientScenicBackground.kt +++ b/hummingbird/androidPatientApp/app/src/main/java/net/acumenus/hummingbird/patient/ui/PatientScenicBackground.kt @@ -1,4 +1,4 @@ -package net.acumenus.nightingale.ui +package net.acumenus.hummingbird.patient.ui import androidx.annotation.DrawableRes import androidx.compose.foundation.Image @@ -13,7 +13,7 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Brush import androidx.compose.ui.layout.ContentScale import androidx.compose.ui.res.painterResource -import net.acumenus.nightingale.R +import net.acumenus.hummingbird.patient.R enum class PatientScene(@DrawableRes val drawable: Int) { WELCOME(R.drawable.patient_hummingbird_airy_flight), @@ -26,7 +26,7 @@ enum class PatientScene(@DrawableRes val drawable: Int) { /** * Visual clarity always wins over the decorative scene. A large text scale - * retains a faint, static Nightingale image but uses an almost-opaque surface + * retains a faint, static Hummingbird image but uses an almost-opaque surface * veil so care content and controls remain the primary visual signal. */ internal data class PatientSceneAccessibilityPolicy( diff --git a/hummingbird/androidPatientApp/app/src/main/java/net/acumenus/nightingale/ui/PatientSessionManagementScreen.kt b/hummingbird/androidPatientApp/app/src/main/java/net/acumenus/hummingbird/patient/ui/PatientSessionManagementScreen.kt similarity index 96% rename from hummingbird/androidPatientApp/app/src/main/java/net/acumenus/nightingale/ui/PatientSessionManagementScreen.kt rename to hummingbird/androidPatientApp/app/src/main/java/net/acumenus/hummingbird/patient/ui/PatientSessionManagementScreen.kt index e991a471..efed4927 100644 --- a/hummingbird/androidPatientApp/app/src/main/java/net/acumenus/nightingale/ui/PatientSessionManagementScreen.kt +++ b/hummingbird/androidPatientApp/app/src/main/java/net/acumenus/hummingbird/patient/ui/PatientSessionManagementScreen.kt @@ -1,4 +1,4 @@ -package net.acumenus.nightingale.ui +package net.acumenus.hummingbird.patient.ui import androidx.activity.compose.BackHandler import androidx.compose.foundation.layout.Arrangement @@ -39,9 +39,9 @@ import androidx.compose.ui.semantics.liveRegion import androidx.compose.ui.semantics.semantics import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.unit.dp -import net.acumenus.nightingale.PatientDeviceSessionOperation -import net.acumenus.nightingale.PatientDeviceSessionsState -import net.acumenus.nightingale.data.PatientDeviceSession +import net.acumenus.hummingbird.patient.PatientDeviceSessionOperation +import net.acumenus.hummingbird.patient.PatientDeviceSessionsState +import net.acumenus.hummingbird.patient.data.PatientDeviceSession import java.time.OffsetDateTime import java.time.format.DateTimeFormatter import java.util.Locale @@ -67,7 +67,7 @@ internal fun PatientSessionManagementScreen( IconButton(onClick = onDismiss) { Icon( Icons.AutoMirrored.Outlined.ArrowBack, - contentDescription = "Back to Nightingale", + contentDescription = "Back to Hummingbird", ) } }, @@ -94,7 +94,7 @@ internal fun PatientSessionManagementScreen( modifier = Modifier.semantics { heading() }, ) Text( - text = "Review devices that can currently open your Nightingale account. Device details stay on this screen only.", + text = "Review devices that can currently open your Hummingbird Patient account. Device details stay on this screen only.", style = MaterialTheme.typography.bodyMedium, color = MaterialTheme.colorScheme.onSurfaceVariant, ) @@ -263,7 +263,7 @@ private fun PatientDeviceSessionConfirmation( text = { Text( if (session.current) { - "You will return to the Nightingale sign-in screen on this device. Other signed-in devices are not changed." + "You will return to the Hummingbird Patient sign-in screen on this device. Other signed-in devices are not changed." } else { "${session.deviceLabel()} will need to sign in again. This current device will stay signed in." }, diff --git a/hummingbird/androidPatientApp/app/src/main/res/values-v27/themes.xml b/hummingbird/androidPatientApp/app/src/main/res/values-v27/themes.xml index 8718638e..2634ef23 100644 --- a/hummingbird/androidPatientApp/app/src/main/res/values-v27/themes.xml +++ b/hummingbird/androidPatientApp/app/src/main/res/values-v27/themes.xml @@ -1,5 +1,5 @@ - diff --git a/hummingbird/androidPatientApp/app/src/main/res/values/strings.xml b/hummingbird/androidPatientApp/app/src/main/res/values/strings.xml index b1033de3..4cd9410b 100644 --- a/hummingbird/androidPatientApp/app/src/main/res/values/strings.xml +++ b/hummingbird/androidPatientApp/app/src/main/res/values/strings.xml @@ -1,3 +1,3 @@ - Nightingale + Hummingbird Patient diff --git a/hummingbird/androidPatientApp/app/src/main/res/values/themes.xml b/hummingbird/androidPatientApp/app/src/main/res/values/themes.xml index 32e98ad3..67bc9939 100644 --- a/hummingbird/androidPatientApp/app/src/main/res/values/themes.xml +++ b/hummingbird/androidPatientApp/app/src/main/res/values/themes.xml @@ -1,5 +1,5 @@ - - +