Repository navigation
Salvage the Nightingale governance layer from #92, and stop identity collisions recurring - #111
Merged
Merged
Conversation
This was referenced Jul 27, 2026
sudoshi
enabled auto-merge (squash)
July 28, 2026 00:50
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>
sudoshi
force-pushed
the
feat/nightingale-salvage-from-92
branch
from
July 28, 2026 01:03
4fbecbd to
9baa372
Compare
This was referenced Jul 28, 2026
sudoshi
added a commit
that referenced
this pull request
Jul 28, 2026
…ted) (#119) * feat(flow4d): bounded pathway-serving machinery (build; activation gated) The reusable machinery to serve REAL (non-overlay) governed-pathway progress in the 4D Navigator for the Nightingale investor-demo cohort (PR #118). Build only — no prod writes; catalog activation + assignment remain gated behind [SU] signoff. - Flow4dPathwayAssignmentService: layers a pathway_instance + LOS-driven milestone statuses onto an existing #118 cohort grant (source_encounter_id + pathway:read); requires the catalog already activated (instantiate enforces approved+active + release signed-off), so it fails loudly otherwise. - care-pathways:promote-executable-layer: the missing draft→approved milestone/stage promotion (scope REQUIRED, --confirm REQUIRED; never promotes the whole catalog). review_state is not a protected content column, so the immutability trigger allows it. - flow4d:assign-demo-pathways: reads the cohort SSOT (NightingaleDemoCohort::MEMBERS demo_username→pathway_key) + its grants and assigns each patient their pathway. - Flow4dPathwayServingTest: proves the full loop end-to-end in the isolated test DB — adopt → author(draft) → promote → activate → cohort grant → assign → serve real progress (PathwayInstanceReadService, demo=false). This IS the prod runbook, proven. Scoped to the 5 cohort pathways (all evidence-verified). 5 tests green; Pint clean. Note: PatientJourneyEndpointTest event-count assertion is a pre-existing LOCAL-only failure from #111's identity changes (main CI is green) — unrelated to this branch. * fix(test): anchor PatientJourneyEndpointTest events to now (calendar bit-rot) The journey seeds hardcoded 2026-07-25 events, but the journey window is now-72h; once the calendar advanced >72h past those dates the events aged out of range and the ordered-story assertion dropped from 3 to 1. Anchors the seed to now (gaps preserved: 210 + 525 min) so it's stable. Unrelated to the serving machinery; unblocks feature-1 CI (and fixes the same latent failure on main).
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
#92 built an independent Nightingale product foundation. It was superseded by
#101, which renamed the existing patient app in place — that app was already
feature-complete and is what ships to TestFlight today. #92's app scaffolds are
therefore not adopted: 19 iOS files with no features and no networking, so
taking them would have replaced a working patient application with a launch
screen.
Its backend governance layer, documentation, and privacy work are a different
matter, and are salvaged here.
Activation and disclosure gates
20 pure classes plus tests. The activation gate evaluates five independent,
conjunctive preconditions — clinical approval, content release, feature
activation, pilot enrollment, source connector — any one of which holds the
whole evaluation. No route, container binding, config reader, source query,
audit writer, or native caller.
This models exactly the separation that was missing when tracing why no
projection reaches a patient: clinical approval and content release as separate
conditions rather than a single flag.
A config collision this avoids
#92 adds a 44-line
config/nightingale.phpdeclaring a foundation namespace.mainalready has a 271-lineconfig/nightingale.php— the live file thedeployed 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 marked section instead;
config:showconfirms everyserving value is unchanged.
Privacy manifest, corrected
Neither shipped iOS app had a
PrivacyInfo.xcprivacy. Apple requires one forApp Store submission. #92's declared an empty
NSPrivacyCollectedDataTypes—true of a foundation with no networking, false for an app that authenticates
and exchanges clinical content. Copying it verbatim would have filed an
inaccurate declaration, which is worse than filing none.
Verified against source:
UserDefaultsis the only required-reason APIcategory in the target, so
CA92.1alone is correct, andNSPrivacyTrackingfalse is already enforced by
check-no-tracking-technologies.sh. Thecollected-data entries are deliberately conservative and carry an in-file note
that they need product and privacy sign-off before submission.
63 governance documents
Threat and hazard model, privacy release controls, accessibility layout and
language-readiness matrices, dependency inventory, contract ownership matrix,
identity recovery and protected-state decisions, background rights review,
controlled pilot manifest, and the product identity checklist.
Nine cite
nightingale/iosApppaths that were never adopted. Rather than editsixty-three documents or drop the nine, the README carries a substitution table
and states which parts of #92 were salvaged and which were not.
The guard that should have existed
scripts/ci/verify-app-identity-uniqueness.shfails CI when two applicationsdeclare the same identifier within a platform. Per-platform, not global — one
product legitimately declares the same identifier once for iOS and once for
Android.
Verified both directions: passes on the current tree (7 iOS, 2 Android), and
exits 1 naming all four collisions against a reconstruction of the
#92-plus-#101 merged tree. That is the failure that should have happened in
July, before either shipped.
Verification
tracking-technology guards pass
config:show nightingaleconfirms live values unchangedNot carried across
nightingale/iosApp,nightingale/androidApp, and every deletion ofhummingbird/…PatientApp.