'deterministic', * 'summary' => string, * 'inconsistencies' => string[], * 'scope_drift' => string[], * 'notes' => string[], * 'closure_confidence' => 'high' | 'medium' | 'low' | 'n/a', * 'closure_confidence_explanation' => string, * 'needs_human_arbitration' => bool, * 'human_arbitration_message' => string, // verbatim user-facing line * 'human_arbitration_triggers' => string[], // reasons firing the flag * ] */ public static function evaluate( array $trace ) { $arbs = isset( $trace['arbitrations'] ) ? (array) $trace['arbitrations'] : []; $links = isset( $trace['plan_links'] ) ? (array) $trace['plan_links'] : []; $events = isset( $trace['acceptance'] ) ? (array) $trace['acceptance'] : []; $basic = $trace['closure_state_basic'] ?? null; $authority = $trace['closure_authority_valid'] ?? null; // Event-type flags $has_claim = false; $has_dispute = false; $has_affirm = false; $has_revoke = false; foreach ( $events as $e ) { if ( $e->event_type === 'result_claimed' ) $has_claim = true; if ( $e->event_type === 'acceptance_disputed' ) $has_dispute = true; if ( $e->event_type === 'acceptance_affirmed' ) $has_affirm = true; if ( $e->event_type === 'acceptance_revoked' ) $has_revoke = true; } $inconsistencies = []; $drift = []; $notes = []; // --- Inconsistencies --- // Closure authority invalid: latest acceptance_affirmed by non-stakeholder if ( $authority === false ) { $latest_affirm = null; foreach ( array_reverse( $events ) as $e ) { if ( $e->event_type === 'acceptance_affirmed' ) { $latest_affirm = $e; break; } } if ( $latest_affirm ) { $inconsistencies[] = "Closure authority invalid: acceptance_affirmed event #{$latest_affirm->id} authored by actor_role='{$latest_affirm->actor_role}' (must be 'stakeholder' for authoritative closure)."; } } // projection_type vs plan_link count mismatch foreach ( $arbs as $arb ) { $arb_id = (int) $arb->id; $arb_links = count( array_filter( $links, function ( $l ) use ( $arb_id ) { return (int) $l->arbitration_id === $arb_id; } ) ); if ( $arb->projection_type === 'scope-direct-no-plan' && $arb_links > 0 ) { $inconsistencies[] = "Arbitration #{$arb->id} declares projection_type='scope-direct-no-plan' but has {$arb_links} plan link(s) — inconsistent."; } if ( in_array( $arb->projection_type, [ 'scope-direct', 'scope-decomposed', 'scope-merged' ], true ) && $arb_links === 0 ) { $inconsistencies[] = "Arbitration #{$arb->id} declares projection_type='{$arb->projection_type}' but has no plan links — projection promised a Plan but none recorded."; } } // Affirmation without claim (closure path is incomplete) if ( $has_affirm && ! $has_claim ) { $inconsistencies[] = "Acceptance was affirmed without any prior result_claimed event — closure path is incomplete."; } // prior_event_id chain integrity $event_ids = array_map( function ( $e ) { return (int) $e->id; }, $events ); foreach ( $events as $e ) { if ( $e->prior_event_id !== null && $e->prior_event_id !== '' ) { $prior = (int) $e->prior_event_id; if ( $prior > 0 && ! in_array( $prior, $event_ids, true ) ) { $inconsistencies[] = "Event #{$e->id} references prior_event_id={$prior}, which is not in this Demand's event chain."; } } } // --- Scope drift --- if ( count( $arbs ) > 1 ) { $drift[] = "Demand has " . count( $arbs ) . " arbitrations recorded — review for re-interpretation across arbitration moments."; } foreach ( $arbs as $arb ) { if ( ! in_array( $arb->projection_type, [ 'scope-direct', 'scope-direct-no-plan' ], true ) ) { $drift[] = "Arbitration #{$arb->id} uses projection_type='{$arb->projection_type}' — explicit scope transformation. Verify the Plan honors the original Demand intent."; } if ( $arb->truth_class === 'derived' ) { $drift[] = "Arbitration #{$arb->id} has truth_class='derived' — Demand was reconstructed from Plan-side evidence. Original scope is not directly verifiable."; } } // --- Notes (informational) --- if ( $has_dispute && $has_affirm ) { $notes[] = "This Demand experienced a dispute before its current affirmation. Review the dispute → affirmation transition for completeness of resolution."; } if ( $has_revoke ) { $notes[] = "An acceptance_revoked event is present — a previously-affirmed acceptance was withdrawn."; } if ( ! empty( $events ) && ! $has_claim && ! $has_dispute && ! $has_affirm && ! $has_revoke ) { $notes[] = "Acceptance events present but none of the standard types (claim/dispute/affirm/revoke) — review event_type values."; } // --- Summary + confidence --- $summary = self::build_summary( $arbs, $links, $events, $basic, $authority ); $confidence = self::compute_confidence( $basic, $authority, $has_dispute, $has_revoke, count( $inconsistencies ), count( $drift ), count( $events ) ); // --- Needs Human Arbitration (v0.4.1) --- $human = self::compute_human_arbitration( $arbs, $events, $authority ); return [ 'mode' => self::MODE, 'summary' => $summary, 'inconsistencies' => $inconsistencies, 'scope_drift' => $drift, 'notes' => $notes, 'closure_confidence' => $confidence['level'], 'closure_confidence_explanation' => $confidence['explanation'], 'needs_human_arbitration' => $human['required'], 'human_arbitration_message' => $human['message'], 'human_arbitration_triggers' => $human['triggers'], ]; } /** * Determine whether this trace requires human arbitration. * * Triggers (any one is sufficient): * 1. closure_authority_valid = false (latest acceptance_affirmed by non-stakeholder). * 2. arbitration carries truth_class='derived' AND no acceptance events exist * (Demand was reconstructed from Plan-side evidence and never confirmed). * 3. multiple arbitrations on the same Demand (re-interpretation requires * human reconciliation). * 4. latest event is acceptance_disputed (dispute is unresolved). * * The human arbitration verdict is the deterministic layer's escalation * signal. It is independent of closure_confidence — a Demand can be * "open" and still require human arbitration if the conditions warrant. */ private static function compute_human_arbitration( $arbs, $events, $authority ) { $triggers = []; // 1. closure_authority_valid = false if ( $authority === false ) { $triggers[] = "closure_authority_valid is false (acceptance_affirmed event was authored by a non-stakeholder actor)."; } // 2. derived arbitration without acceptance events $has_derived_arb = false; foreach ( $arbs as $a ) { if ( ( $a->truth_class ?? null ) === 'derived' ) { $has_derived_arb = true; break; } } if ( $has_derived_arb && count( $events ) === 0 ) { $triggers[] = "Demand is derived (truth_class='derived' on arbitration) and has no acceptance events — original stakeholder intent is unverified."; } // 3. multiple arbitrations on the same Demand if ( count( $arbs ) > 1 ) { $triggers[] = count( $arbs ) . " arbitrations exist on the same Demand — re-interpretation requires human reconciliation."; } // 4. latest event is acceptance_disputed if ( ! empty( $events ) ) { $latest = end( $events ); if ( ( $latest->event_type ?? null ) === 'acceptance_disputed' ) { $triggers[] = "Latest event is acceptance_disputed — dispute is unresolved and requires human follow-up before closure can be trusted."; } } return [ 'required' => ! empty( $triggers ), 'message' => 'This trace requires human arbitration before closure can be trusted.', 'triggers' => $triggers, ]; } private static function build_summary( $arbs, $links, $events, $basic, $authority ) { $arb_n = count( $arbs ); $link_n = count( $links ); $event_n = count( $events ); if ( $arb_n === 0 && $event_n === 0 ) { return "No arbitration and no acceptance events for this Demand. The Demand may be unrecognized by SA-Orchestration, or the surface_ref may be wrong."; } $bits = []; $bits[] = "{$arb_n} arbitration" . ( $arb_n === 1 ? '' : 's' ); $bits[] = "{$link_n} plan link" . ( $link_n === 1 ? '' : 's' ); $bits[] = "{$event_n} acceptance event" . ( $event_n === 1 ? '' : 's' ); $event_seq = ''; if ( $event_n > 0 ) { $sequence = []; foreach ( $events as $e ) $sequence[] = $e->event_type; $event_seq = ' Sequence: ' . implode( ' → ', $sequence ) . '.'; } $closure_phrase = ''; if ( $basic === 'closed' ) { if ( $authority === true ) { $closure_phrase = ' Currently closed (stakeholder-affirmed).'; } elseif ( $authority === false ) { $closure_phrase = ' Marked closed but closure authority is invalid (non-stakeholder affirmed).'; } else { $closure_phrase = ' Currently closed.'; } } elseif ( is_string( $basic ) && strpos( $basic, 'open:' ) === 0 ) { $closure_phrase = " Currently open ({$basic})."; } elseif ( $basic === 'no-events' ) { $closure_phrase = ' No acceptance events.'; } return implode( ', ', $bits ) . '.' . $event_seq . $closure_phrase; } private static function compute_confidence( $basic, $authority, $has_dispute, $has_revoke, $inc_count, $drift_count, $event_count ) { // No events — confidence not meaningful if ( $event_count === 0 || $basic === 'no-events' ) { return [ 'level' => 'n/a', 'explanation' => 'No acceptance events to evaluate; closure confidence is not yet meaningful.', ]; } // Open states if ( is_string( $basic ) && strpos( $basic, 'open:' ) === 0 ) { return [ 'level' => 'low', 'explanation' => "Demand is currently {$basic}; closure has not been claimed and confirmed.", ]; } // Closed but authority invalid if ( $basic === 'closed' && $authority === false ) { return [ 'level' => 'low', 'explanation' => 'Closed by non-stakeholder actor; closure_authority_valid=false. Treat as not authoritatively closed.', ]; } // Closed with stakeholder authority if ( $basic === 'closed' && $authority === true ) { if ( $inc_count > 0 ) { return [ 'level' => 'low', 'explanation' => "Closed by stakeholder, but {$inc_count} inconsistenc" . ( $inc_count === 1 ? 'y' : 'ies' ) . ' detected. Review before treating as final.', ]; } $reasons = []; if ( $has_dispute ) $reasons[] = 'a prior dispute was recorded'; if ( $has_revoke ) $reasons[] = 'an acceptance_revoked event exists'; if ( $drift_count ) $reasons[] = "{$drift_count} potential scope-drift indicator" . ( $drift_count === 1 ? '' : 's' ); if ( count( $reasons ) === 0 ) { return [ 'level' => 'high', 'explanation' => 'Closed by stakeholder with a clean event chain and no scope drift detected.', ]; } return [ 'level' => 'medium', 'explanation' => 'Closed by stakeholder, but ' . implode( '; ', $reasons ) . '. Review history before treating as routine.', ]; } // Fall-through (unrecognized basic state) return [ 'level' => 'low', 'explanation' => "Unrecognized closure_state_basic value: '{$basic}'." ]; } }