File size: 3,320 Bytes
f51c224
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
<?php
defined( 'ABSPATH' ) || exit;

/**
 * Strategy LLM Review orchestrator.
 *
 * Layer 3 of the review stack. Delegates to the active provider (resolved
 * via SA_Orch_LLM_Providers::get_active_provider()) and adds metadata
 * about which provider produced the result.
 *
 * Constraints (enforced by contract, not code):
 *   - The Strategy Review NEVER overrides closure_state or
 *     needs_human_arbitration. Those are governed by the deterministic
 *     audit and ultimately by human arbitration.
 *   - Strategy Review is purely advisory.
 */
class SA_Orch_Strategy {

    /**
     * Run a Strategy Review against the trace + deterministic audit.
     *
     * Returns the provider's review_trace() output augmented with:
     *   - provider_id      (string)
     *   - provider_name    (string)
     *   - provider_error   (string, optional — present if the provider threw)
     */
    public static function review( array $trace, array $deterministic_audit ): array {
        $configured  = SA_Orch_LLM_Providers::get_active_provider();
        $configured_id   = $configured->id();
        $configured_name = $configured->name();

        $fallback_used = false;
        $error_message = null;
        $used          = $configured;

        try {
            $review = $configured->review_trace( $trace, $deterministic_audit );
        } catch ( \Throwable $e ) {
            // Fallback to simulated on any error from the configured provider
            $fallback      = new SA_Orch_LLM_Provider_Simulated();
            $review        = $fallback->review_trace( $trace, $deterministic_audit );
            $error_message = $e->getMessage();
            $fallback_used = true;
            $used          = $fallback;
        }

        // Defensive: enforce required keys
        $review = wp_parse_args( $review, [
            'summary'               => '',
            'agreement_with_audit'  => 'partial',
            'additional_risks'      => [],
            'suggested_next_action' => '',
            'confidence'            => 'low',
        ] );

        // Conservative confidence floor (v0.5.2): when the deterministic audit
        // cannot evaluate closure (closure_confidence='n/a' — i.e. no acceptance
        // events recorded), the strategy layer MUST collapse to 'low'. Prevents
        // LLM-shaped over-confidence on Demands with no acceptance evidence.
        // Cross-cutting: applied here so any provider (including future ones)
        // gets it for free, regardless of what the model returned.
        if ( ( $deterministic_audit['closure_confidence'] ?? '' ) === 'n/a'
             && in_array( $review['confidence'], [ 'medium', 'high' ], true ) ) {
            $review['confidence'] = 'low';
        }

        // Provider used to actually produce this output
        $review['provider_id']   = $used->id();
        $review['provider_name'] = $used->name();

        // Configured provider (what the operator selected)
        $review['configured_provider_id']   = $configured_id;
        $review['configured_provider_name'] = $configured_name;

        // Fallback transparency
        $review['fallback_used'] = $fallback_used;
        if ( $error_message !== null ) {
            $review['provider_error'] = $error_message;
        }

        return $review;
    }
}