@@ -2679,6 +2679,48 @@ fn compact_decision_reason(reason: &str, width: usize) -> String {
26792679 } else {
26802680 "budget"
26812681 }
2682+ } else if lower == "cost budget exceeded" {
2683+ if width >= 13 {
2684+ "cost budget"
2685+ } else {
2686+ "cost"
2687+ }
2688+ } else if lower == "context threshold exceeded" {
2689+ if width >= 13 {
2690+ "context limit"
2691+ } else {
2692+ "context"
2693+ }
2694+ } else if lower == "failed response threshold exceeded" {
2695+ if width >= 12 {
2696+ "failed limit"
2697+ } else {
2698+ "failed"
2699+ }
2700+ } else if lower == "incomplete response threshold exceeded" {
2701+ if width >= 16 {
2702+ "incomplete limit"
2703+ } else {
2704+ "incomplete"
2705+ }
2706+ } else if lower == "unknown response threshold exceeded" {
2707+ if width >= 13 {
2708+ "unknown limit"
2709+ } else {
2710+ "unknown"
2711+ }
2712+ } else if lower == "accounting anomaly threshold exceeded" {
2713+ if width >= 16 {
2714+ "accounting limit"
2715+ } else {
2716+ "accounting"
2717+ }
2718+ } else if lower == "model mismatch observed" {
2719+ if width >= 14 {
2720+ "model mismatch"
2721+ } else {
2722+ "model"
2723+ }
26822724 } else if lower == "upstream errors" {
26832725 if width >= 15 {
26842726 "upstream errors"
@@ -5975,6 +6017,18 @@ mod tests {
59756017 } ) ,
59766018 [ "budget exceeded" , "budget" ] ,
59776019 ) ,
6020+ (
6021+ "Blocked" ,
6022+ decide ( & ObservedSessionFacts {
6023+ policy_block : Some ( PolicyBlockFacts {
6024+ reason : "context threshold exceeded" . to_string ( ) ,
6025+ recovery_action : "narrow next prompt" . to_string ( ) ,
6026+ ..Default :: default ( )
6027+ } ) ,
6028+ ..Default :: default ( )
6029+ } ) ,
6030+ [ "context threshold exceeded" , "context limit" ] ,
6031+ ) ,
59786032 (
59796033 "Cooldown" ,
59806034 decide ( & ObservedSessionFacts {
@@ -7086,8 +7140,7 @@ mod tests {
70867140 )
70877141 . to_string( ) ] ;
70887142 let ( watch_url, _watch_request_rx) = serve_sse_chunks_once ( chunks) ;
7089- let ( api_url, api_request_rx) = serve_json_once (
7090- r#"{
7143+ let report = r#"{
70917144 "schema_version": 1,
70927145 "session_id": "session_ready",
70937146 "redacted": true,
@@ -7104,12 +7157,34 @@ mod tests {
71047157 "local_estimated_cost_dollars": 0.0
71057158 },
71067159 "signals": {"response_statuses": {"completed": 1}},
7160+ "flight_recorder": [
7161+ {
7162+ "turn": 1,
7163+ "timestamp": "2026-04-30T12:00:01Z",
7164+ "status": "completed",
7165+ "requested_model": "gpt-codex-fixture",
7166+ "served_model": "gpt-codex-fixture",
7167+ "model_mismatch": false,
7168+ "input_tokens": 1000,
7169+ "cached_input_tokens": 500,
7170+ "uncached_input_tokens": 500,
7171+ "output_tokens": 100,
7172+ "reasoning_output_tokens": 20,
7173+ "local_total_tokens": 1100,
7174+ "context_fill_percent": 12.5,
7175+ "context_window_tokens": 8000,
7176+ "duration_ms": 42
7177+ }
7178+ ],
71077179 "evidence": [],
71087180 "timeline": [],
71097181 "recommendations": [],
71107182 "caveats": ["Evidence is limited to local Envoy-observed Codex Responses traffic."]
7111- }"# ,
7112- ) ;
7183+ }"# ;
7184+ let report_json: serde_json:: Value =
7185+ serde_json:: from_str ( report) . expect ( "watch postmortem fixture" ) ;
7186+ assert ! ( !super :: postmortem_flight_recorder_terminal_lines( & report_json) . is_empty( ) ) ;
7187+ let ( api_url, api_request_rx) = serve_json_once ( report) ;
71137188 let options = WatchRenderOptions {
71147189 base_url : api_url,
71157190 no_signals : false ,
0 commit comments