@@ -53,12 +53,38 @@ function resolveGateMode({ promoted }) {
5353}
5454
5555function createAbortVisibility ( { rollbackTriggered, promoted, reason } ) {
56- const aborted = Boolean ( rollbackTriggered ) && ! promoted ;
56+ const aborted = Boolean ( rollbackTriggered ) ;
5757 return {
5858 decisionPath : 'PROMOTION_GATE_ABORT_VISIBILITY' ,
5959 rollbackTriggered : Boolean ( rollbackTriggered ) ,
6060 aborted,
61- reason : aborted ? 'ROLLBACK_ABORTED_PROMOTION' : String ( reason || '' )
61+ reason : aborted
62+ ? ( promoted ? 'ROLLBACK_ABORT_VISIBLE' : 'ROLLBACK_ABORTED_PROMOTION' )
63+ : String ( reason || '' )
64+ } ;
65+ }
66+
67+ function createValidationChecklist ( evaluation , cycleState ) {
68+ const modeVisible = typeof evaluation ?. mode === 'string' && evaluation . mode . length > 0 ;
69+ const handoffVisible = isPlainObject ( evaluation ?. handoff )
70+ && evaluation . handoff . decisionPath === 'PROMOTION_GATE_HANDOFF' ;
71+ const abortVisible = isPlainObject ( evaluation ?. abort )
72+ && evaluation . abort . decisionPath === 'PROMOTION_GATE_ABORT_VISIBILITY' ;
73+
74+ cycleState . passiveSeen = cycleState . passiveSeen || evaluation . mode === 'passive' ;
75+ cycleState . authoritativeSeen = cycleState . authoritativeSeen || evaluation . mode === 'authoritative' ;
76+ cycleState . abortSeen = cycleState . abortSeen || evaluation . abort ?. aborted === true ;
77+ cycleState . fullCycleReached = cycleState . passiveSeen && cycleState . authoritativeSeen && cycleState . abortSeen ;
78+
79+ return {
80+ checklistPath : 'PROMOTION_GATE_VALIDATION_CLOSEOUT' ,
81+ modeVisible,
82+ handoffVisible,
83+ abortVisible,
84+ passiveSeen : cycleState . passiveSeen ,
85+ authoritativeSeen : cycleState . authoritativeSeen ,
86+ abortSeen : cycleState . abortSeen ,
87+ fullCycleReached : cycleState . fullCycleReached
6288 } ;
6389}
6490
@@ -68,6 +94,7 @@ function createPromotionGate(options = {}) {
6894 const stabilityWindowFrames = asPositiveInteger ( options . stabilityWindowFrames , 3 ) ;
6995 const logger = typeof options . logger === 'function' ? options . logger : null ;
7096 const onEvaluation = typeof options . onEvaluation === 'function' ? options . onEvaluation : null ;
97+ const onValidation = typeof options . onValidation === 'function' ? options . onValidation : null ;
7198
7299 let promoted = Boolean ( options . initiallyPromoted ) ;
73100 let stableFrames = promoted ? stabilityWindowFrames : 0 ;
@@ -83,6 +110,12 @@ function createPromotionGate(options = {}) {
83110 lastEvaluationAtMs : null ,
84111 lastPromotionAtMs : promoted ? Number ( now ( ) ) : null
85112 } ;
113+ const validationCycle = {
114+ passiveSeen : ! promoted ,
115+ authoritativeSeen : promoted ,
116+ abortSeen : false ,
117+ fullCycleReached : false
118+ } ;
86119
87120 function getMetrics ( ) {
88121 return {
@@ -180,6 +213,7 @@ function createPromotionGate(options = {}) {
180213 reason : lastReason ,
181214 metrics : getMetrics ( )
182215 } ;
216+ evaluation . validation = createValidationChecklist ( evaluation , validationCycle ) ;
183217
184218 lastEvaluation = evaluation ;
185219
@@ -192,6 +226,9 @@ function createPromotionGate(options = {}) {
192226 if ( onEvaluation ) {
193227 onEvaluation ( evaluation ) ;
194228 }
229+ if ( onValidation ) {
230+ onValidation ( evaluation . validation , evaluation ) ;
231+ }
195232
196233 return evaluation ;
197234 }
0 commit comments