Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -208,6 +208,7 @@ jobs:
php scripts/verify-hummingbird-transport-security.php
php scripts/generate-hummingbird-route-inventory.php --check
bash scripts/check-no-tracking-technologies.sh
bash scripts/ci/verify-app-identity-uniqueness.sh

- name: Verify immutable release snapshots
run: |
Expand Down
15 changes: 15 additions & 0 deletions app/Nightingale/Activation/NightingaleActivationDisposition.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<?php

namespace App\Nightingale\Activation;

/**
* Route-free result of evaluating the five independent activation gates.
*
* ContinueToOperationSpecificReleaseEvaluation is deliberately weaker than
* access, authorization, disclosure, pilot approval, or production readiness.
*/
enum NightingaleActivationDisposition: string
{
case Hold = 'hold';
case ContinueToOperationSpecificReleaseEvaluation = 'continue_to_operation_specific_release_evaluation';
}
32 changes: 32 additions & 0 deletions app/Nightingale/Activation/NightingaleActivationGate.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
<?php

namespace App\Nightingale\Activation;

/**
* Keeps approval, content, feature, enrollment, and source deployment
* independent and conjunctive.
*
* This pure gate accepts no identifiers or records and has no route, container
* binding, configuration reader, source query, audit writer, or native caller.
*/
final class NightingaleActivationGate
{
public function evaluate(
NightingaleClinicalApprovalState $clinicalApproval,
NightingaleContentReleaseState $contentRelease,
NightingaleFeatureActivationState $featureActivation,
NightingalePilotEnrollmentState $pilotEnrollment,
NightingaleSourceConnectorState $sourceConnector,
): NightingaleActivationDisposition {
if (! $clinicalApproval->permitsFurtherEvaluation()
|| ! $contentRelease->permitsFurtherEvaluation()
|| ! $featureActivation->permitsFurtherEvaluation()
|| ! $pilotEnrollment->permitsFurtherEvaluation()
|| ! $sourceConnector->permitsFurtherEvaluation()
) {
return NightingaleActivationDisposition::Hold;
}

return NightingaleActivationDisposition::ContinueToOperationSpecificReleaseEvaluation;
}
}
21 changes: 21 additions & 0 deletions app/Nightingale/Activation/NightingaleClinicalApprovalState.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<?php

namespace App\Nightingale\Activation;

/**
* Institution-owned clinical approval prerequisite.
*
* Recorded may be emitted only by a future adapter that validates an exact,
* current, in-scope approval record. It is not inferred from source data,
* pathway verification, clinician use, or another activation prerequisite.
*/
enum NightingaleClinicalApprovalState: string
{
case Absent = 'absent';
case Recorded = 'recorded';

public function permitsFurtherEvaluation(): bool
{
return $this === self::Recorded;
}
}
21 changes: 21 additions & 0 deletions app/Nightingale/Activation/NightingaleContentReleaseState.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<?php

namespace App\Nightingale\Activation;

/**
* Patient-content release prerequisite.
*
* Released means only that a future content service validated an exact,
* audience-specific release. It does not imply clinical approval, feature
* activation, patient enrollment, connector deployment, or disclosure.
*/
enum NightingaleContentReleaseState: string
{
case Unreleased = 'unreleased';
case Released = 'released';

public function permitsFurtherEvaluation(): bool
{
return $this === self::Released;
}
}
20 changes: 20 additions & 0 deletions app/Nightingale/Activation/NightingaleFeatureActivationState.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<?php

namespace App\Nightingale\Activation;

/**
* Product feature-switch prerequisite.
*
* Enabled is an independently governed state and cannot substitute for any
* clinical, content, enrollment, source, identity, or authorization decision.
*/
enum NightingaleFeatureActivationState: string
{
case Disabled = 'disabled';
case Enabled = 'enabled';

public function permitsFurtherEvaluation(): bool
{
return $this === self::Enabled;
}
}
20 changes: 20 additions & 0 deletions app/Nightingale/Activation/NightingalePilotEnrollmentState.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<?php

namespace App\Nightingale\Activation;

/**
* Patient/cohort pilot-enrollment prerequisite.
*
* Enrolled may be emitted only after a future pilot service validates scope,
* expiry, exclusions, consent, support coverage, and withdrawal state.
*/
enum NightingalePilotEnrollmentState: string
{
case NotEnrolled = 'not_enrolled';
case Enrolled = 'enrolled';

public function permitsFurtherEvaluation(): bool
{
return $this === self::Enrolled;
}
}
20 changes: 20 additions & 0 deletions app/Nightingale/Activation/NightingaleSourceConnectorState.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<?php

namespace App\Nightingale\Activation;

/**
* Authoritative-source connector deployment prerequisite.
*
* Deployed may be emitted only after a future source boundary validates the
* approved adapter, facility scope, health, version, and rollback posture.
*/
enum NightingaleSourceConnectorState: string
{
case Undeployed = 'undeployed';
case Deployed = 'deployed';

public function permitsFurtherEvaluation(): bool
{
return $this === self::Deployed;
}
}
35 changes: 35 additions & 0 deletions app/Nightingale/Disclosure/NightingaleDisclosureDisposition.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
<?php

namespace App\Nightingale\Disclosure;

/**
* Publicly observable disposition of the route-free disclosure foundation.
*
* ContinueToGovernedProjectionEvaluation is not an authorization or release
* decision. A future operation must still pass source, field-release,
* freshness, language, correction, audit-before-disclosure, and
* serialization-boundary recheck gates.
*/
enum NightingaleDisclosureDisposition: string
{
case WithholdNotFound = 'withhold_not_found';
case ContinueToGovernedProjectionEvaluation = 'continue_to_governed_projection_evaluation';

/**
* The complete public failure tuple. It intentionally has no message,
* internal reason, identifier, retry hint, redirect, or variable field.
*
* @return null|array{status: int, code: string, cache_control: string}
*/
public function publicFailure(): ?array
{
return match ($this) {
self::WithholdNotFound => [
'status' => 404,
'code' => 'not_found',
'cache_control' => 'private, no-store, max-age=0',
],
self::ContinueToGovernedProjectionEvaluation => null,
};
}
}
20 changes: 20 additions & 0 deletions app/Nightingale/Disclosure/NightingaleEncounterBindingState.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<?php

namespace App\Nightingale\Disclosure;

/**
* Whether the requested opaque handle remains bound to the request-scoped
* current inpatient context at the disclosure boundary.
*
* No raw handle or encounter identifier crosses this domain seam.
*/
enum NightingaleEncounterBindingState: string
{
case MatchesCurrentContext = 'matches_current_context';
case WrongEncounter = 'wrong_encounter';

public function permitsGovernedEvaluation(): bool
{
return $this === self::MatchesCurrentContext;
}
}
32 changes: 32 additions & 0 deletions app/Nightingale/Disclosure/NightingaleGenericNonDisclosureGate.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
<?php

namespace App\Nightingale\Disclosure;

use App\Nightingale\EncounterAccess\NightingalePreconditionDisposition;

/**
* Collapses all non-disclosable request states into one public disposition.
*
* The gate is intentionally pure, identifier-free, route-free, and
* side-effect-free. It cannot query a patient source, emit an audit event,
* disclose content, or activate a product surface.
*/
final class NightingaleGenericNonDisclosureGate
{
public function evaluate(
NightingalePreconditionDisposition $preconditions,
NightingaleRelationshipState $relationship,
NightingaleEncounterBindingState $encounterBinding,
NightingaleResourceState $resource,
): NightingaleDisclosureDisposition {
if ($preconditions !== NightingalePreconditionDisposition::ContinueToGovernedEvaluation
|| ! $relationship->permitsGovernedEvaluation()
|| ! $encounterBinding->permitsGovernedEvaluation()
|| ! $resource->permitsGovernedEvaluation()
) {
return NightingaleDisclosureDisposition::WithholdNotFound;
}

return NightingaleDisclosureDisposition::ContinueToGovernedProjectionEvaluation;
}
}
25 changes: 25 additions & 0 deletions app/Nightingale/Disclosure/NightingaleRelationshipState.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
<?php

namespace App\Nightingale\Disclosure;

/**
* Request-scoped relationship result produced by a future approved
* authorization adapter.
*
* These states deliberately contain no principal, patient, grant, encounter,
* source, or representative identifier. Active means only that this
* prerequisite may proceed; it never grants disclosure by itself.
*/
enum NightingaleRelationshipState: string
{
case Unknown = 'unknown';
case Active = 'active';
case Revoked = 'revoked';
case Expired = 'expired';
case CrossPrincipal = 'cross_principal';

public function permitsGovernedEvaluation(): bool
{
return $this === self::Active;
}
}
23 changes: 23 additions & 0 deletions app/Nightingale/Disclosure/NightingaleResourceState.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<?php

namespace App\Nightingale\Disclosure;

/**
* Release-aware resource lookup result supplied only after a future
* operation-specific policy evaluates existence and eligibility together.
*
* Omitted intentionally combines unknown, absent, closed, withheld,
* unreleased, retracted, and otherwise non-disclosable resources at this
* public boundary. Internal causes belong only in a separately approved,
* content-free audit implementation.
*/
enum NightingaleResourceState: string
{
case Released = 'released';
case Omitted = 'omitted';

public function permitsGovernedEvaluation(): bool
{
return $this === self::Released;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
<?php

namespace App\Nightingale\EncounterAccess;

use App\Nightingale\Identity\NightingaleIdentityBoundary;
use App\Nightingale\Inpatient\NightingaleInpatientContextSource;

/**
* Combines only the two prerequisite ports established in the foundation ADR.
*
* It never grants access and never returns data. Any non-positive or
* unavailable prerequisite fails closed.
*/
final class NightingaleEncounterAccessPreconditionGate
{
public function evaluate(
NightingaleIdentityBoundary $identity,
NightingaleInpatientContextSource $inpatientSource,
): NightingalePreconditionDisposition {
if (! $identity->state()->permitsGovernedEvaluation()
|| ! $inpatientSource->state()->permitsGovernedEvaluation()
) {
return NightingalePreconditionDisposition::Withhold;
}

return NightingalePreconditionDisposition::ContinueToGovernedEvaluation;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<?php

namespace App\Nightingale\EncounterAccess;

/**
* This disposition is intentionally weaker than an authorization decision.
* ContinueToGovernedEvaluation means that later identity-link, grant, purpose,
* cardinality, race, audit, and serialization gates still must pass.
*/
enum NightingalePreconditionDisposition: string
{
case Withhold = 'withhold';
case ContinueToGovernedEvaluation = 'continue_to_governed_evaluation';
}
16 changes: 16 additions & 0 deletions app/Nightingale/Identity/NightingaleIdentityBoundary.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<?php

namespace App\Nightingale\Identity;

/**
* Request-scoped port implemented only after the Nightingale identity,
* proofing, session, recovery, and representative decisions are approved.
*
* The port intentionally accepts no legacy principal, token, grant, or source
* identifier. A future ingress adapter must create a request-scoped
* implementation without leaking credentials into this domain boundary.
*/
interface NightingaleIdentityBoundary
{
public function state(): NightingaleIdentityState;
}
22 changes: 22 additions & 0 deletions app/Nightingale/Identity/NightingaleIdentityState.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<?php

namespace App\Nightingale\Identity;

/**
* Request-scoped identity state for the independent Nightingale realm.
*
* This enum is not an authentication mechanism. In particular, VerifiedSelf
* means only that a future, approved identity adapter completed its own
* evaluation. It does not grant encounter access or authorize disclosure.
*/
enum NightingaleIdentityState: string
{
case Unavailable = 'unavailable';
case Denied = 'denied';
case VerifiedSelf = 'verified_self';

public function permitsGovernedEvaluation(): bool
{
return $this === self::VerifiedSelf;
}
}
Loading
Loading