Repository navigation
Conversation
…ient-product # Conflicts: # .appledeploy.example # .gitleaks.toml # 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
PR #92 ("Establish independent Nightingale patient product foundation") already builds nightingale/backgrounds/ properly: a README, a backgrounds.v1.json manifest, and optimised drawable-nodpi derivatives. These seven raw files were undocumented candidates with uneven provenance — two Unsplash-named, the rest wallpaper-site downloads with no recorded licence — so keeping them would add a second, worse copy of work that is already done. The paper, plan, devlog, and brand renders are unaffected. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
| This PR (#92) | #101 (merged, shipped) | |
|---|---|---|
| Strategy | New nightingale/iosApp + nightingale/androidApp |
Renamed hummingbird/iosPatientApp in place |
| Apple bundle ID | net.acumenus.nightingale |
net.acumenus.nightingale |
| Old patient app | Deletes it | Keeps it, renamed |
The merge does not conflict, and that is the problem
git merge-tree origin/main <this branch> reports 0 conflicting paths. The
deletions here target paths that no longer exist under those names — #101
renamed them — so git treats them as no-ops. Inspecting the resulting tree:
nightingale/iosApp 19 files ← added by this PR
nightingale/androidApp 50 files ← added by this PR
hummingbird/androidPatientApp 64 files ← survives, renamed by #101
Merging as-is leaves two Nightingale applications in the repository sharing one
bundle identifier, with the App Store Connect record (app id 6794950746) and
live TestFlight build 202607271938 already bound to the #101 tree.
Context, not a request to change direction
For what it is worth, this PR is the better-engineered approach — an
independent product boundary, the identity registry in
PRODUCT-IDENTITY-AND-SUPPORT-NAMING-CHECKLIST-2026-07-26.md, and
provenance-tracked backgrounds. #101 was the expedient path to a shippable
build. If this PR becomes canonical, the reconciliation is: delete the renamed
hummingbird/iosPatientApp and hummingbird/androidPatientApp, and repoint
.appledeploy (NIGHTINGALE_DIR, NIGHTINGALE_XCODEPROJ, NIGHTINGALE_SCHEME)
at nightingale/iosApp before the next ./testflight.sh ship nightingale.
Also note #110 originally added raw nightingale/backgrounds/*.jpg; those have
been removed from that PR in favour of the curated set here.
Flagging rather than resolving — which of the two architectures wins is a
product decision, not a merge-conflict one.
Neither shipped iOS app carries a PrivacyInfo.xcprivacy. Apple requires a required-reason API manifest for App Store submission; TestFlight tolerates its absence, the store does not. PR #92 had written one, so this salvages it — with a correction. That version declared an empty NSPrivacyCollectedDataTypes. True of its foundation build, which had no networking at all; false here, where the app authenticates and exchanges clinical content. Copying it verbatim would have filed an inaccurate declaration with Apple, which is worse than filing none. Verified against the source rather than assumed: - UserDefaults is the only required-reason API category used (Networking/PatientSecureStore.swift). No FileManager timestamp, disk space, boot time, or active-keyboard API appears anywhere in the target, so CA92.1 alone is correct. - NSPrivacyTracking false is accurate; scripts/check-no-tracking-technologies.sh already enforces the absence of tracking SDKs in CI. The collected-data entries reflect what the client demonstrably moves: a patient context identifier and bearer credentials, released clinical projections, and patient-authored message content. Whether each counts as "collected" also depends on backend retention, so they are deliberately conservative and carry an in-file note that they need product and privacy sign-off before submission. Over-declaring is safe; under-declaring is a misrepresentation. xcodegen bundles it into the target (4 references in the generated project); the xcode-project, patient-boundary, and tracking-technology guards pass. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
63 documents: threat and hazard model, privacy release controls, accessibility layout and language-readiness matrices, foundation dependency inventory, contract ownership and authorization matrix, identity recovery and protected-state decisions, background asset governance and rights review, the controlled pilot manifest, and the product identity and support naming checklist — plus the release evidence under docs/evidence. These are the decision records that make a patient-facing clinical product defensible under review, and they survive the architecture change that superseded the PR they came from. Nine of them cite nightingale/iosApp and nightingale/androidApp paths that were never adopted. Rather than edit sixty-three documents or drop the nine, the README carries a substitution table and states plainly which parts of #92 were salvaged and which were not, so a reader is not misled about where the code actually lives. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
On 2026-07-27 two independent efforts each declared net.acumenus.nightingale: #101 renamed the existing patient app in place, #92 built a new foundation tree. Both were internally consistent. Nothing compared them, and the merge produced zero conflicts — git cannot see that two project.yml files claim the same App Store record. The collision was found by reading diffs by hand, after one of them had already shipped to TestFlight. Enforces per-platform uniqueness rather than global uniqueness: one product legitimately declares the same identifier once for iOS and once for Android. What must never happen is the same identifier twice within a platform, because that is two applications competing for one store listing. Verified both directions. Against the current tree it passes, reporting 7 iOS bundle identifiers and 2 Android applicationIds. Against a reconstruction of the #92-plus-#101 merged tree it exits 1 and names all four collisions with the declaring files, which is the failure that should have happened in July. Runs alongside the existing transport-security and tracking-technology guards in the Backend quality job. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
Superseded by #101 (shipped) and #111 (salvage). The architecture decision went the other way: the patient app was renamed in Closing rather than merging, because merging was actively unsafe. It reports The work here was not wasted. #111 salvages:
Not salvaged: the two app scaffolds, and the deletions of the patient app trees. |
PR #92 built an independent Nightingale product foundation, then fell behind main when the patient product was renamed in place instead (#101). The app tree it added is a scaffold with no features or networking, so it is superseded — but its backend governance layer is not, and is worth keeping. Ports 20 pure classes plus their test: App\Nightingale\Activation a conjunctive gate over five independent preconditions — clinical approval, content release, feature activation, pilot enrollment, source connector — any one of which holds the whole evaluation App\Nightingale\Disclosure a generic non-disclosure gate over relationship, encounter binding, and resource state App\Nightingale\EncounterAccess, Identity, Inpatient precondition gates and unconfigured-by-default boundaries These are pure: no route, container binding, config reader, source query, audit writer, or native caller. They model exactly the separation that was missing when tracing why no projection reaches a patient — clinical approval and content release as independent conditions rather than one flag. Note the config collision this avoids. #92 adds a 44-line config/nightingale.php declaring a foundation namespace; main already has a 271-line config/nightingale.php, the live file the deployed backend reads for 48 environment variables. Merging #92 as-is would have had one silently replace the other. The foundation keys are merged into the existing file under a clearly marked section instead, leaving every serving value untouched. Verified: 23 salvaged tests pass (149 assertions); the patient suite still passes 301 tests (4,290 assertions); config:show confirms the live values are unchanged. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Neither shipped iOS app carries a PrivacyInfo.xcprivacy. Apple requires a required-reason API manifest for App Store submission; TestFlight tolerates its absence, the store does not. PR #92 had written one, so this salvages it — with a correction. That version declared an empty NSPrivacyCollectedDataTypes. True of its foundation build, which had no networking at all; false here, where the app authenticates and exchanges clinical content. Copying it verbatim would have filed an inaccurate declaration with Apple, which is worse than filing none. Verified against the source rather than assumed: - UserDefaults is the only required-reason API category used (Networking/PatientSecureStore.swift). No FileManager timestamp, disk space, boot time, or active-keyboard API appears anywhere in the target, so CA92.1 alone is correct. - NSPrivacyTracking false is accurate; scripts/check-no-tracking-technologies.sh already enforces the absence of tracking SDKs in CI. The collected-data entries reflect what the client demonstrably moves: a patient context identifier and bearer credentials, released clinical projections, and patient-authored message content. Whether each counts as "collected" also depends on backend retention, so they are deliberately conservative and carry an in-file note that they need product and privacy sign-off before submission. Over-declaring is safe; under-declaring is a misrepresentation. xcodegen bundles it into the target (4 references in the generated project); the xcode-project, patient-boundary, and tracking-technology guards pass. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
63 documents: threat and hazard model, privacy release controls, accessibility layout and language-readiness matrices, foundation dependency inventory, contract ownership and authorization matrix, identity recovery and protected-state decisions, background asset governance and rights review, the controlled pilot manifest, and the product identity and support naming checklist — plus the release evidence under docs/evidence. These are the decision records that make a patient-facing clinical product defensible under review, and they survive the architecture change that superseded the PR they came from. Nine of them cite nightingale/iosApp and nightingale/androidApp paths that were never adopted. Rather than edit sixty-three documents or drop the nine, the README carries a substitution table and states plainly which parts of #92 were salvaged and which were not, so a reader is not misled about where the code actually lives. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
On 2026-07-27 two independent efforts each declared net.acumenus.nightingale: #101 renamed the existing patient app in place, #92 built a new foundation tree. Both were internally consistent. Nothing compared them, and the merge produced zero conflicts — git cannot see that two project.yml files claim the same App Store record. The collision was found by reading diffs by hand, after one of them had already shipped to TestFlight. Enforces per-platform uniqueness rather than global uniqueness: one product legitimately declares the same identifier once for iOS and once for Android. What must never happen is the same identifier twice within a platform, because that is two applications competing for one store listing. Verified both directions. Against the current tree it passes, reporting 7 iOS bundle identifiers and 2 Android applicationIds. Against a reconstruction of the #92-plus-#101 merged tree it exits 1 and names all four collisions with the declaring files, which is the failure that should have happened in July. Runs alongside the existing transport-security and tracking-technology guards in the Backend quality job. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…collisions recurring (#111) * Salvage the Nightingale activation and disclosure gates from #92 PR #92 built an independent Nightingale product foundation, then fell behind main when the patient product was renamed in place instead (#101). The app tree it added is a scaffold with no features or networking, so it is superseded — but its backend governance layer is not, and is worth keeping. Ports 20 pure classes plus their test: App\Nightingale\Activation a conjunctive gate over five independent preconditions — clinical approval, content release, feature activation, pilot enrollment, source connector — any one of which holds the whole evaluation App\Nightingale\Disclosure a generic non-disclosure gate over relationship, encounter binding, and resource state App\Nightingale\EncounterAccess, Identity, Inpatient precondition gates and unconfigured-by-default boundaries These are pure: no route, container binding, config reader, source query, audit writer, or native caller. They model exactly the separation that was missing when tracing why no projection reaches a patient — clinical approval and content release as independent conditions rather than one flag. Note the config collision this avoids. #92 adds a 44-line config/nightingale.php declaring a foundation namespace; main already has a 271-line config/nightingale.php, the live file the deployed backend reads for 48 environment variables. Merging #92 as-is would have had one silently replace the other. The foundation keys are merged into the existing file under a clearly marked section instead, leaving every serving value untouched. Verified: 23 salvaged tests pass (149 assertions); the patient suite still passes 301 tests (4,290 assertions); config:show confirms the live values are unchanged. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * Add the Apple privacy manifest the shipped patient app was missing Neither shipped iOS app carries a PrivacyInfo.xcprivacy. Apple requires a required-reason API manifest for App Store submission; TestFlight tolerates its absence, the store does not. PR #92 had written one, so this salvages it — with a correction. That version declared an empty NSPrivacyCollectedDataTypes. True of its foundation build, which had no networking at all; false here, where the app authenticates and exchanges clinical content. Copying it verbatim would have filed an inaccurate declaration with Apple, which is worse than filing none. Verified against the source rather than assumed: - UserDefaults is the only required-reason API category used (Networking/PatientSecureStore.swift). No FileManager timestamp, disk space, boot time, or active-keyboard API appears anywhere in the target, so CA92.1 alone is correct. - NSPrivacyTracking false is accurate; scripts/check-no-tracking-technologies.sh already enforces the absence of tracking SDKs in CI. The collected-data entries reflect what the client demonstrably moves: a patient context identifier and bearer credentials, released clinical projections, and patient-authored message content. Whether each counts as "collected" also depends on backend retention, so they are deliberately conservative and carry an in-file note that they need product and privacy sign-off before submission. Over-declaring is safe; under-declaring is a misrepresentation. xcodegen bundles it into the target (4 references in the generated project); the xcode-project, patient-boundary, and tracking-technology guards pass. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * Salvage the Nightingale governance documentation from #92 63 documents: threat and hazard model, privacy release controls, accessibility layout and language-readiness matrices, foundation dependency inventory, contract ownership and authorization matrix, identity recovery and protected-state decisions, background asset governance and rights review, the controlled pilot manifest, and the product identity and support naming checklist — plus the release evidence under docs/evidence. These are the decision records that make a patient-facing clinical product defensible under review, and they survive the architecture change that superseded the PR they came from. Nine of them cite nightingale/iosApp and nightingale/androidApp paths that were never adopted. Rather than edit sixty-three documents or drop the nine, the README carries a substitution table and states plainly which parts of #92 were salvaged and which were not, so a reader is not misled about where the code actually lives. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * Fail CI when two applications claim one store identifier On 2026-07-27 two independent efforts each declared net.acumenus.nightingale: #101 renamed the existing patient app in place, #92 built a new foundation tree. Both were internally consistent. Nothing compared them, and the merge produced zero conflicts — git cannot see that two project.yml files claim the same App Store record. The collision was found by reading diffs by hand, after one of them had already shipped to TestFlight. Enforces per-platform uniqueness rather than global uniqueness: one product legitimately declares the same identifier once for iOS and once for Android. What must never happen is the same identifier twice within a platform, because that is two applications competing for one store listing. Verified both directions. Against the current tree it passes, reporting 7 iOS bundle identifiers and 2 Android applicationIds. Against a reconstruction of the #92-plus-#101 merged tree it exits 1 and names all four collisions with the declaring files, which is the failure that should have happened in July. Runs alongside the existing transport-security and tracking-technology guards in the Backend quality job. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
Outcome
Establishes Nightingale as an independent patient-facing product while preserving Hummingbird as the staff operations product and retaining the former Hummingbird Patient app only as deprecated migration/reference evidence.
7967931d642bab16807bd7a9fa1bd3c2283d3b28Product and brand boundary
nightingale/iosAppandnightingale/androidApproots usingnet.acumenus.nightingale.net.acumenus.hummingbirdand restores the deprecated reference patient app tonet.acumenus.hummingbird.patient.Fail-closed background rights review
Patient-safe offline foundation
FLAG_SECURE, backup/transfer exclusions, cleartext denial, an exact iOS privacy manifest, and runtime scans rejecting networking, tracking, logging, clipboard, and Release test-hook drift.Namespace and activation-state separation
net.acumenus.nightingale, including Android presentation keys and both native future-session-binding identifiers.hold; the one all-positive combination can only continue to later operation-specific release evaluation.Contract, parity, and migration evidence
/api/nightingale/v1and the held/inpatient-contextsoperation while mechanically rejecting aliases, proxies, redirects, and legacy API fallbacks.Default-off controlled-pilot manifest
Generic non-disclosure foundation
404/not_found/private, no-store, max-age=0tuple.Explicitly authorized production sample patient
The operator explicitly authorized one bounded production sample derived from the deprecated Hummingbird Patient reference patient. Evidence is retained in
docs/evidence/nightingale/production-sample-patient-2026-07-27/README.md.demo-nightingale-reference-inpatient, active/not discharged, medical-surgical unit 85, no bed, acuity 2.Nightingale Reference Patient, synthetic, productnightingale, statuspending, inactive, and without email, phone, password, verification, or authentication history.source_template_product=hummingbird_patientandsource_template_owner=hummingbird-patient-reference-identity-provisioner-v1.Required investor-demo patient cohort
demo1throughdemo5; the shared runtime password is intentionally omitted from source, documentation, CI, and evidence and must be stored only as a one-way hash.Current-main reconciliation
Evidence:
docs/evidence/nightingale/current-main-reconciliation-2026-07-27/README.md.Merged current
origin/mainatea14d2bd2be9656555dfba35da2dbf28e74730e3in merge commitbcaae3da.Preserved the unrelated Flow 4D plan update from
f63e5994.Found that main's PR Rename the patient product from Hummingbird Patient to Nightingale #101 rename repurposed the deprecated Hummingbird Patient roots and identities as Nightingale, conflicting with the accepted independent-product architecture.
Forward-reverted only that incompatible rename in
c11d462cb5c62e399139caf0538ed9def1f1d441; published history was not rewritten.Exact tree proof at
c11d462cshows the reconciled branch differs from verified pre-merge head21e4de17in exactly one path, the unrelated Flow 4D plan, and that path is byte-identical tof63e5994.Revalidated independent Nightingale iOS/Android roots and
net.acumenus.nightingaleidentity, deprecated Hummingbird Patient roots andnet.acumenus.hummingbird.patientidentity, route-free Nightingale backend, and independent distribution source.This reconciliation performed no production database access or mutation and closes no additional functional checklist item.
Subsequently merged current
mainthrough98d9bf98f2e811f6b39c0d14bb5e0ea1b5ea7317, including the independentfrontend-buildCI job and its documentation. The runtime, contract, native-client, fixture, and sample-patient trees remained unchanged after emulator acceptance.The first exact-SHA run exposed one Gitleaks 8.28 false positive in the new upstream devlog: the ordinary prose phrase
notification/deliveryfollowingAPI paths. Extended the existing exception only to that exact path and exact captured phrase.A checksum-verified pinned Gitleaks 8.28 scan then passed all 1,083 locally visible commits and a clean archived checkout. The generic credential rule remains active everywhere else.
Validation
Local reacceptance
Hosted exact-SHA CI
CI run 30306442357 completed successfully on exact head
7967931d642bab16807bd7a9fa1bd3c2283d3b28: 23/23 jobs succeeded, including the independent frontend build, frontend tests, backend quality and five backend shards, security, Arena, DAST, browser, mobile brand assets, Nightingale contract, both Nightingale native apps, both Hummingbird staff apps, and both deprecated Hummingbird Patient apps.Explicit holds
Apart from the single recorded inert production sample exception above, this draft does not authorize merge, application deployment, migration, additional database mutation, additional patient creation, identity/provider selection, enrollment or recovery workflow, identity-to-encounter binding, patient-source query, clinical content, live Today/My Path/Care Team rendering, representatives, messaging, notifications, signed distribution, store submission, pilot, or production activation.
Still required are released operation implementations; named identity/source and field-level clinical/content approvals; patient-advisor, accessibility, language/interpreter, privacy/security, clinical-safety, legal/HIM, nursing, medical-staff, pharmacy, support, operations, and release review; physical-device and assistive-technology validation; full WCAG 2.2 AA work; penetration/red-team/tabletop and supply-chain review; distribution rights; production-like integration/failover testing; and a separately authorized pilot go/no-go record.