/ scripts / flight_deck.py
flight_deck.py
   1  #!/usr/bin/env python3
   2  """
   3  Sovereign Flight Deck - Real-time operator situational awareness.
   4  
   5  A streaming terminal dashboard that shows the pilot the state of the entire
   6  consciousness system - value accruing, resonance permeating, context advancing,
   7  and the graph being built.
   8  
   9  ## Flight Metaphor
  10  
  11  You're piloting a helicopter in formation with other helicopters (Claude instances).
  12  The flight deck shows:
  13  
  14  | Instrument | Aviation | Sovereign OS |
  15  |------------|----------|--------------|
  16  | Altimeter | Height | Altitude (philosophical ↔ operational) |
  17  | Airspeed | Velocity | Pull rate (convergence → action) |
  18  | Heading | Direction | Primary gravity well |
  19  | VSI | Climb/descent | ΔF (alignment change) |
  20  | Attitude | Pitch/roll | F score (level = aligned) |
  21  | Fuel | Time remaining | Sats balance |
  22  | Engines | Power output | Active threads |
  23  | Radar | Other aircraft | Fleet (mesh peers) |
  24  | Warnings | Alerts | Resonance/hygiene alerts |
  25  
  26  ## Usage
  27  
  28      # Full dashboard (streaming)
  29      python3 scripts/flight_deck.py
  30  
  31      # Minimal mode (one-shot status)
  32      python3 scripts/flight_deck.py --minimal
  33  
  34      # Watch resonance alerts only
  35      python3 scripts/flight_deck.py --alerts
  36  """
  37  
  38  import json
  39  import sys
  40  import os
  41  import time
  42  import urllib.request
  43  import urllib.error
  44  from pathlib import Path
  45  from datetime import datetime, timedelta
  46  from dataclasses import dataclass, field
  47  from typing import Optional, List, Dict, Any, Tuple
  48  import threading
  49  import signal
  50  
  51  # Paths
  52  SOVEREIGN_OS = Path(__file__).parent.parent
  53  SESSIONS_DIR = SOVEREIGN_OS / "sessions"
  54  SOVEREIGN_HOME = Path.home() / ".sovereign"
  55  
  56  FO_STATE = SESSIONS_DIR / "FO-STATE.json"
  57  META_FO_STATE = SESSIONS_DIR / "META-FO-STATE.json"
  58  LIVE_COMPRESSION = SESSIONS_DIR / "LIVE-COMPRESSION.md"
  59  GRAPH_DATA = SOVEREIGN_HOME / "graph-data.json"
  60  RESONANCE_ALERTS = SESSIONS_DIR / "RESONANCE-ALERTS"
  61  
  62  MESH_URL = "http://localhost:7778"
  63  
  64  # ═══════════════════════════════════════════════════════════════════════════
  65  # MAJOR RESONANCE THRESHOLDS
  66  # Uses existing resonance.py system: urgency >= 0.7 = should_bubble
  67  # ═══════════════════════════════════════════════════════════════════════════
  68  
  69  # Urgency threshold for major resonance (from resonance.py: should_bubble = 0.7)
  70  RESONANCE_URGENCY_THRESHOLD = 0.70
  71  
  72  # Importance threshold for insights that bypass normal resonance flow
  73  RESONANCE_IMPORTANCE_THRESHOLD = 0.85
  74  
  75  # Divergence range that indicates edge discovery (D 0.30-0.50 is edge zone)
  76  RESONANCE_DIVERGENCE_MIN = 0.30
  77  RESONANCE_DIVERGENCE_MAX = 0.50
  78  
  79  # Delta F threshold - how much alignment improvement triggers major
  80  RESONANCE_DELTA_F_THRESHOLD = -0.10  # 10% improvement in F
  81  
  82  # Cooldown between major resonance broadcasts (prevents spam)
  83  RESONANCE_COOLDOWN_MINUTES = 30
  84  
  85  # Track last broadcast time
  86  _last_major_broadcast: Optional[datetime] = None
  87  # Track already-broadcast resonance IDs
  88  _broadcast_resonance_ids: set = set()
  89  
  90  
  91  # ═══════════════════════════════════════════════════════════════════════════
  92  # ANSI COLORS & FORMATTING
  93  # ═══════════════════════════════════════════════════════════════════════════
  94  
  95  class Colors:
  96      RESET = "\033[0m"
  97      BOLD = "\033[1m"
  98      DIM = "\033[2m"
  99  
 100      # Foreground
 101      BLACK = "\033[30m"
 102      RED = "\033[31m"
 103      GREEN = "\033[32m"
 104      YELLOW = "\033[33m"
 105      BLUE = "\033[34m"
 106      MAGENTA = "\033[35m"
 107      CYAN = "\033[36m"
 108      WHITE = "\033[37m"
 109  
 110      # Bright foreground
 111      BRIGHT_GREEN = "\033[92m"
 112      BRIGHT_YELLOW = "\033[93m"
 113      BRIGHT_RED = "\033[91m"
 114      BRIGHT_CYAN = "\033[96m"
 115      BRIGHT_MAGENTA = "\033[95m"
 116  
 117      # Background
 118      BG_RED = "\033[41m"
 119      BG_GREEN = "\033[42m"
 120      BG_YELLOW = "\033[43m"
 121      BG_BLUE = "\033[44m"
 122  
 123  
 124  def clear_screen():
 125      os.system('clear' if os.name == 'posix' else 'cls')
 126  
 127  
 128  def move_cursor(row: int, col: int):
 129      print(f"\033[{row};{col}H", end="")
 130  
 131  
 132  def hide_cursor():
 133      print("\033[?25l", end="")
 134  
 135  
 136  def show_cursor():
 137      print("\033[?25h", end="")
 138  
 139  
 140  # ═══════════════════════════════════════════════════════════════════════════
 141  # DATA COLLECTION
 142  # ═══════════════════════════════════════════════════════════════════════════
 143  
 144  @dataclass
 145  class FlightState:
 146      """Current state of the flight (aggregated from all sources)."""
 147      # Timestamp
 148      updated: datetime = field(default_factory=datetime.now)
 149  
 150      # === PRIMARY INSTRUMENTS ===
 151      altitude: str = "tactical"  # philosophical/strategic/tactical/operational
 152      altitude_numeric: float = 0.5  # 0=operational, 1=philosophical
 153      pull_rate: float = 0.5  # 0-1 convergence toward action
 154      heading: str = ""  # Primary gravity well
 155      free_energy: float = 0.1  # Current F score
 156      delta_f: float = 0.0  # Change in F
 157  
 158      # === FUEL & ECONOMICS ===
 159      sats_balance: int = 0
 160      sats_burned_session: int = 0
 161      efficiency_multiplier: float = 1.0
 162      roi_percent: float = 0.0
 163      value_created: int = 0  # Total value this session
 164      effective_rate: float = 0.0  # $/hr effective
 165      v_score_avg: float = 0.0  # Average V-score
 166      session_duration_min: int = 0  # Minutes in session
 167      work_items_done: int = 0  # Completed items
 168      insights_captured: int = 0  # Wisdom extracted
 169  
 170      # === ENGINES (THREADS) ===
 171      active_threads: int = 0
 172      focal_thread: str = ""
 173      thread_ids: List[str] = field(default_factory=list)
 174  
 175      # === FLEET (MESH PEERS) ===
 176      fleet_size: int = 0
 177      fleet_peers: List[str] = field(default_factory=list)
 178      mesh_online: bool = False
 179  
 180      # === GRAPH (KNOWLEDGE BUILDING) ===
 181      graph_nodes: int = 0
 182      graph_edges: int = 0
 183      nodes_added_session: int = 0
 184      edges_added_session: int = 0
 185  
 186      # === AXIOM ACTIVITY ===
 187      axiom_activity: Dict[str, int] = field(default_factory=dict)
 188      primary_axiom: str = ""
 189  
 190      # === ALERTS ===
 191      resonance_alerts: List[Dict] = field(default_factory=list)
 192      hygiene_warnings: List[str] = field(default_factory=list)
 193      major_resonance: Optional[Dict] = None  # High-energy resonance to highlight
 194  
 195      # === ISSUES (Error Tracking) ===
 196      error_count_session: int = 0
 197      warning_count_session: int = 0
 198      issue_types: Dict[str, int] = field(default_factory=dict)  # type -> count
 199      recent_issues: List[str] = field(default_factory=list)  # Last 3 issues
 200  
 201      # === MOMENTUM ===
 202      gravity_wells: List[str] = field(default_factory=list)
 203      rising_concepts: List[str] = field(default_factory=list)
 204      open_threads: List[str] = field(default_factory=list)
 205  
 206  
 207  class FlightDataCollector:
 208      """Collects data from all sources into FlightState."""
 209  
 210      def __init__(self):
 211          self._last_graph_nodes = 0
 212          self._last_graph_edges = 0
 213          self._session_start_nodes = None
 214          self._session_start_edges = None
 215          self._last_f = 0.1
 216          self._alert_cache: List[Dict] = []
 217          self._alert_last_check = datetime.min
 218  
 219      def collect(self) -> FlightState:
 220          """Collect current state from all sources."""
 221          state = FlightState(updated=datetime.now())
 222  
 223          # Collect in parallel-ish (they're all local reads)
 224          self._collect_fo_state(state)
 225          self._collect_meta_fo(state)
 226          self._collect_mesh(state)
 227          self._collect_graph(state)
 228          self._collect_alerts(state)
 229          self._collect_live_compression(state)
 230          self._collect_economics(state)
 231          self._collect_issues(state)
 232          self._detect_major_resonance(state)
 233  
 234          return state
 235  
 236      def _collect_fo_state(self, state: FlightState):
 237          """Collect from FO-STATE.json."""
 238          if not FO_STATE.exists():
 239              return
 240  
 241          try:
 242              with open(FO_STATE) as f:
 243                  data = json.load(f)
 244  
 245              # Axiom activity
 246              state.axiom_activity = data.get("axiom_activity", {})
 247              if state.axiom_activity:
 248                  state.primary_axiom = max(state.axiom_activity, key=state.axiom_activity.get)
 249  
 250              # Costs
 251              costs = data.get("costs", {})
 252              sats = costs.get("sats", {})
 253              state.sats_balance = sats.get("subscription_sats", 0)
 254              state.efficiency_multiplier = costs.get("efficiency", {}).get("api_efficiency", 1.0)
 255  
 256              # Free energy
 257              if "free_energy" in data:
 258                  new_f = data["free_energy"]
 259                  state.delta_f = new_f - self._last_f
 260                  self._last_f = new_f
 261                  state.free_energy = new_f
 262  
 263          except Exception as e:
 264              pass
 265  
 266      def _collect_meta_fo(self, state: FlightState):
 267          """Collect from META-FO-STATE.json (Mission Control)."""
 268          if not META_FO_STATE.exists():
 269              return
 270  
 271          try:
 272              with open(META_FO_STATE) as f:
 273                  data = json.load(f)
 274  
 275              # Active threads
 276              instances = data.get("instances", {})
 277              state.active_threads = len(instances)
 278              state.thread_ids = list(instances.keys())
 279  
 280              # Focal thread
 281              if instances:
 282                  # Most recent is focal
 283                  sorted_instances = sorted(
 284                      instances.items(),
 285                      key=lambda x: x[1].get("last_update", ""),
 286                      reverse=True
 287                  )
 288                  if sorted_instances:
 289                      state.focal_thread = sorted_instances[0][0]
 290  
 291              # Gravity wells from meta
 292              state.gravity_wells = data.get("gravity_wells", [])[:5]
 293  
 294          except Exception as e:
 295              pass
 296  
 297      def _collect_mesh(self, state: FlightState):
 298          """Collect from mesh network."""
 299          try:
 300              req = urllib.request.Request(f"{MESH_URL}/", method="GET")
 301              with urllib.request.urlopen(req, timeout=2) as resp:
 302                  data = json.loads(resp.read().decode())
 303                  state.mesh_online = True
 304                  state.fleet_size = data.get("peers", 0) + 1  # Include self
 305          except:
 306              state.mesh_online = False
 307              state.fleet_size = 1
 308  
 309      def _collect_graph(self, state: FlightState):
 310          """Collect from graph data."""
 311          if not GRAPH_DATA.exists():
 312              return
 313  
 314          try:
 315              with open(GRAPH_DATA) as f:
 316                  data = json.load(f)
 317  
 318              state.graph_nodes = len(data.get("nodes", []))
 319              state.graph_edges = len(data.get("edges", []))
 320  
 321              # Track session delta
 322              if self._session_start_nodes is None:
 323                  self._session_start_nodes = state.graph_nodes
 324                  self._session_start_edges = state.graph_edges
 325  
 326              state.nodes_added_session = state.graph_nodes - self._session_start_nodes
 327              state.edges_added_session = state.graph_edges - self._session_start_edges
 328  
 329          except Exception as e:
 330              pass
 331  
 332      def _collect_alerts(self, state: FlightState):
 333          """Collect resonance alerts."""
 334          if not RESONANCE_ALERTS.exists():
 335              return
 336  
 337          # Only check every 10 seconds
 338          now = datetime.now()
 339          if (now - self._alert_last_check).seconds < 10:
 340              state.resonance_alerts = self._alert_cache
 341              return
 342  
 343          self._alert_last_check = now
 344  
 345          try:
 346              # Get most recent alerts (last 5 minutes)
 347              cutoff = now - timedelta(minutes=5)
 348              alerts = []
 349  
 350              for filepath in sorted(RESONANCE_ALERTS.glob("*.md"), reverse=True)[:10]:
 351                  # Parse timestamp from filename
 352                  name = filepath.stem
 353                  try:
 354                      # Format: 20260116-175809-shared_concept
 355                      ts_str = name[:15]
 356                      ts = datetime.strptime(ts_str, "%Y%m%d-%H%M%S")
 357                      if ts > cutoff:
 358                          content = filepath.read_text()[:200]
 359                          alerts.append({
 360                              "time": ts,
 361                              "type": name.split("-")[-1] if "-" in name else "unknown",
 362                              "preview": content.split("\n")[0][:50]
 363                          })
 364                  except:
 365                      pass
 366  
 367              self._alert_cache = alerts[:5]
 368              state.resonance_alerts = self._alert_cache
 369  
 370          except Exception as e:
 371              pass
 372  
 373      def _collect_live_compression(self, state: FlightState):
 374          """Collect from LIVE-COMPRESSION.md."""
 375          if not LIVE_COMPRESSION.exists():
 376              return
 377  
 378          try:
 379              content = LIVE_COMPRESSION.read_text()
 380  
 381              # Extract altitude
 382              for line in content.split("\n"):
 383                  if "altitude::" in line.lower():
 384                      alt = line.split("::")[-1].strip().lower()
 385                      state.altitude = alt
 386                      # Convert to numeric
 387                      alt_map = {"operational": 0.0, "tactical": 0.33, "strategic": 0.66, "philosophical": 1.0}
 388                      state.altitude_numeric = alt_map.get(alt, 0.5)
 389  
 390                  elif "pull_rate::" in line.lower() or "convergence::" in line.lower():
 391                      try:
 392                          state.pull_rate = float(line.split("::")[-1].strip())
 393                      except:
 394                          pass
 395  
 396                  elif "heading::" in line.lower() or "focus::" in line.lower():
 397                      state.heading = line.split("::")[-1].strip()[:30]
 398  
 399                  elif "free_energy::" in line.lower():
 400                      try:
 401                          # Format might be "F = 0.15" or just "0.15"
 402                          f_str = line.split("::")[-1].strip()
 403                          if "=" in f_str:
 404                              f_str = f_str.split("=")[-1].strip()
 405                          state.free_energy = float(f_str.split()[0])
 406                      except:
 407                          pass
 408  
 409          except Exception as e:
 410              pass
 411  
 412      def _collect_economics(self, state: FlightState):
 413          """Collect economics/value data from session state."""
 414          # Try session report state first
 415          session_state = SESSIONS_DIR / "session-report-state.json"
 416          if session_state.exists():
 417              try:
 418                  with open(session_state) as f:
 419                      data = json.load(f)
 420  
 421                  state.work_items_done = len(data.get("done_items", []))
 422                  state.insights_captured = len(data.get("insights", []))
 423  
 424                  # Calculate V-score average from done items
 425                  done_items = data.get("done_items", [])
 426                  if done_items:
 427                      v_scores = [item.get("v_score", 0.5) for item in done_items if isinstance(item, dict)]
 428                      if v_scores:
 429                          state.v_score_avg = sum(v_scores) / len(v_scores)
 430              except:
 431                  pass
 432  
 433          # Get session duration from FO state timestamps
 434          if FO_STATE.exists():
 435              try:
 436                  with open(FO_STATE) as f:
 437                      data = json.load(f)
 438  
 439                  costs = data.get("costs", {})
 440                  sats = costs.get("sats", {})
 441  
 442                  # Value created = subscription equivalent
 443                  state.value_created = sats.get("subscription_sats", 0)
 444  
 445                  # ROI and efficiency
 446                  efficiency = costs.get("efficiency", {})
 447                  state.efficiency_multiplier = efficiency.get("api_efficiency", 1.0)
 448                  state.roi_percent = efficiency.get("roi_percent", 0.0)
 449  
 450                  # Calculate effective rate (value per hour)
 451                  # From work_items and estimates
 452                  work_items = data.get("work_items", [])
 453                  total_minutes = sum(item.get("duration_minutes", 30) for item in work_items)
 454                  if total_minutes > 0:
 455                      state.session_duration_min = total_minutes
 456                      hourly_sats = (state.value_created / total_minutes) * 60
 457                      state.effective_rate = hourly_sats / 100_000  # Convert to $ (rough)
 458              except:
 459                  pass
 460  
 461      def _collect_issues(self, state: FlightState):
 462          """Collect error/issue signals from hygiene and logs."""
 463          # Check hygiene warnings
 464          try:
 465              import subprocess
 466              result = subprocess.run(
 467                  ["python3", str(SOVEREIGN_OS / "scripts" / "phoenix_hygiene.py")],
 468                  capture_output=True,
 469                  text=True,
 470                  timeout=5
 471              )
 472  
 473              output = result.stdout + result.stderr
 474  
 475              # Count warnings
 476              if "CRITICAL" in output:
 477                  state.error_count_session += output.count("CRITICAL")
 478                  state.issue_types["critical"] = state.issue_types.get("critical", 0) + 1
 479  
 480              if "WARNING" in output:
 481                  state.warning_count_session += output.count("WARNING")
 482                  state.issue_types["warning"] = state.issue_types.get("warning", 0) + 1
 483  
 484              # Extract specific issues
 485              for line in output.split("\n"):
 486                  if "CRITICAL" in line or "WARNING" in line:
 487                      state.recent_issues.append(line.strip()[:60])
 488                      if len(state.recent_issues) >= 3:
 489                          break
 490  
 491          except Exception:
 492              pass
 493  
 494          # Check FO state for errors
 495          if FO_STATE.exists():
 496              try:
 497                  with open(FO_STATE) as f:
 498                      data = json.load(f)
 499  
 500                  # Track trust errors
 501                  trust_errors = data.get("trust_errors", [])
 502                  if trust_errors:
 503                      state.error_count_session += len(trust_errors)
 504                      state.issue_types["trust"] = len(trust_errors)
 505                      for err in trust_errors[-2:]:
 506                          state.recent_issues.append(f"Trust: {str(err)[:50]}")
 507              except:
 508                  pass
 509  
 510      def _detect_major_resonance(self, state: FlightState):
 511          """
 512          Detect if there's a major resonance worth highlighting.
 513  
 514          Integrates with existing resonance.py system:
 515          - Uses urgency >= 0.70 (should_bubble threshold from Resonance class)
 516          - Checks FO insights for high importance (>0.85) or edge zone (D 0.30-0.50)
 517          - Checks ΔF for large alignment improvements
 518  
 519          Calibrated for 2-5 major alerts per day via:
 520          - 30-minute cooldown between broadcasts
 521          - Deduplication of already-broadcast items
 522          """
 523          global _last_major_broadcast, _broadcast_resonance_ids
 524  
 525          # Cooldown check - don't spam
 526          now = datetime.now()
 527          if _last_major_broadcast:
 528              elapsed = (now - _last_major_broadcast).total_seconds() / 60
 529              if elapsed < RESONANCE_COOLDOWN_MINUTES:
 530                  return
 531  
 532          major_candidates = []
 533  
 534          # Path 1: Check resonance alerts for high-urgency items (from resonance.py)
 535          # These already went through the should_bubble check (urgency >= 0.7)
 536          for alert in state.resonance_alerts:
 537              alert_id = f"{alert.get('time', '')}-{alert.get('type', '')}"
 538              if alert_id in _broadcast_resonance_ids:
 539                  continue
 540  
 541              # Look for urgency in the alert (if resonance.py published it)
 542              urgency = alert.get("urgency", 0)
 543              if urgency >= RESONANCE_URGENCY_THRESHOLD:
 544                  major_candidates.append({
 545                      "time": alert.get("time", now),
 546                      "type": f"resonance_{alert.get('type', 'detected')}",
 547                      "preview": alert.get("preview", "")[:50],
 548                      "score": urgency,
 549                      "reason": f"urgency={urgency:.2f} (should_bubble)",
 550                      "resonance_id": alert_id
 551                  })
 552  
 553          # Path 2: High-importance insights from FO state
 554          if FO_STATE.exists():
 555              try:
 556                  with open(FO_STATE) as f:
 557                      data = json.load(f)
 558  
 559                  for insight in data.get("insights", [])[-5:]:
 560                      importance = insight.get("importance", 0)
 561                      divergence = insight.get("divergence_score", 0)
 562  
 563                      # Create unique ID for this insight
 564                      insight_id = f"{insight.get('timestamp', '')}-{insight.get('content', '')[:20]}"
 565  
 566                      # Skip already-broadcast
 567                      if insight_id in _broadcast_resonance_ids:
 568                          continue
 569  
 570                      # Check importance threshold
 571                      if importance >= RESONANCE_IMPORTANCE_THRESHOLD:
 572                          major_candidates.append({
 573                              "time": datetime.fromisoformat(insight.get("timestamp", now.isoformat())),
 574                              "type": "major_insight",
 575                              "preview": insight.get("content", "")[:50],
 576                              "score": importance,
 577                              "reason": f"importance={importance:.2f}",
 578                              "resonance_id": insight_id
 579                          })
 580  
 581                      # Check edge zone (D 0.30-0.50)
 582                      elif RESONANCE_DIVERGENCE_MIN <= divergence <= RESONANCE_DIVERGENCE_MAX:
 583                          major_candidates.append({
 584                              "time": datetime.fromisoformat(insight.get("timestamp", now.isoformat())),
 585                              "type": "edge_discovery",
 586                              "preview": insight.get("content", "")[:50],
 587                              "score": 0.7 + (0.5 - abs(divergence - 0.4)) * 0.6,  # Peak at D=0.4
 588                              "reason": f"edge_zone D={divergence:.2f}",
 589                              "resonance_id": insight_id
 590                          })
 591              except:
 592                  pass
 593  
 594          # Path 2: Large alignment improvement
 595          if state.delta_f <= RESONANCE_DELTA_F_THRESHOLD:
 596              major_candidates.append({
 597                  "time": now,
 598                  "type": "alignment_breakthrough",
 599                  "preview": f"Alignment improved by {abs(state.delta_f):.0%}",
 600                  "score": min(1.0, abs(state.delta_f) * 5),
 601                  "reason": f"delta_f={state.delta_f:.2f}"
 602              })
 603  
 604          # Path 3: Convergence alerts in resonance feed
 605          for alert in state.resonance_alerts:
 606              alert_type = alert.get("type", "")
 607              preview = alert.get("preview", "").lower()
 608  
 609              if alert_type == "convergence" or "breakthrough" in preview:
 610                  major_candidates.append({
 611                      "time": alert.get("time", now),
 612                      "type": "convergence",
 613                      "preview": alert.get("preview", "")[:50],
 614                      "score": 0.80,
 615                      "reason": "convergence_detected"
 616                  })
 617  
 618          # Select highest-scoring candidate
 619          if major_candidates:
 620              best = max(major_candidates, key=lambda x: x.get("score", 0))
 621              state.major_resonance = best
 622  
 623              # Mark as broadcast so we don't repeat
 624              if "resonance_id" in best:
 625                  _broadcast_resonance_ids.add(best["resonance_id"])
 626  
 627              # Broadcast to mesh
 628              self._broadcast_major_resonance(best)
 629              _last_major_broadcast = now
 630  
 631      def _broadcast_major_resonance(self, resonance: Dict):
 632          """Broadcast major resonance to all mesh peers."""
 633          try:
 634              import socket
 635              hostname = socket.gethostname()
 636  
 637              msg = json.dumps({
 638                  "type": "major_resonance",
 639                  "from": hostname,
 640                  "payload": {
 641                      "time": resonance.get("time", datetime.now()).isoformat() if isinstance(resonance.get("time"), datetime) else str(resonance.get("time", "")),
 642                      "resonance_type": resonance.get("type", "unknown"),
 643                      "preview": resonance.get("preview", ""),
 644                      "score": resonance.get("score", 0),
 645                      "reason": resonance.get("reason", "")
 646                  },
 647                  "timestamp": datetime.now().isoformat()
 648              }).encode('utf-8')
 649  
 650              req = urllib.request.Request(
 651                  f"{MESH_URL}/broadcast",
 652                  data=msg,
 653                  headers={"Content-Type": "application/json"},
 654                  method="POST"
 655              )
 656  
 657              with urllib.request.urlopen(req, timeout=3) as resp:
 658                  pass  # Fire and forget
 659  
 660          except Exception as e:
 661              pass  # Mesh may be unavailable
 662  
 663  
 664  # ═══════════════════════════════════════════════════════════════════════════
 665  # DISPLAY RENDERING
 666  # ═══════════════════════════════════════════════════════════════════════════
 667  
 668  class FlightDeckRenderer:
 669      """Renders the flight deck to terminal."""
 670  
 671      WIDTH = 80
 672      HEIGHT = 35
 673  
 674      def __init__(self):
 675          self._frame_count = 0
 676  
 677      def render(self, state: FlightState) -> str:
 678          """Render full flight deck."""
 679          lines = []
 680  
 681          # Major resonance banner (if any) - TOP PRIORITY VISIBILITY
 682          if state.major_resonance:
 683              lines.extend(self._render_major_resonance(state))
 684              lines.append("")
 685  
 686          # Header
 687          lines.extend(self._render_header(state))
 688          lines.append("")
 689  
 690          # Main instruments (top row)
 691          lines.extend(self._render_primary_instruments(state))
 692          lines.append("")
 693  
 694          # Economics & Value (new section)
 695          lines.extend(self._render_economics(state))
 696          lines.append("")
 697  
 698          # Fleet & Graph (middle row)
 699          lines.extend(self._render_fleet_and_graph(state))
 700          lines.append("")
 701  
 702          # Issues / Errors (new section)
 703          if state.error_count_session > 0 or state.warning_count_session > 0:
 704              lines.extend(self._render_issues(state))
 705              lines.append("")
 706  
 707          # Axiom activity
 708          lines.extend(self._render_axioms(state))
 709          lines.append("")
 710  
 711          # Alerts & Resonance
 712          lines.extend(self._render_alerts(state))
 713          lines.append("")
 714  
 715          # Footer
 716          lines.extend(self._render_footer(state))
 717  
 718          self._frame_count += 1
 719          return "\n".join(lines)
 720  
 721      def _render_header(self, state: FlightState) -> List[str]:
 722          C = Colors
 723          mesh_status = f"{C.GREEN}MESH{C.RESET}" if state.mesh_online else f"{C.RED}MESH{C.RESET}"
 724          fleet_str = f"Fleet: {state.fleet_size}" if state.mesh_online else "SOLO"
 725  
 726          return [
 727              f"{C.CYAN}{C.BOLD}{'═' * self.WIDTH}{C.RESET}",
 728              f"{C.CYAN}{C.BOLD}  SOVEREIGN FLIGHT DECK{C.RESET}  │  {mesh_status} {fleet_str}  │  {state.updated.strftime('%H:%M:%S')}",
 729              f"{C.CYAN}{C.BOLD}{'═' * self.WIDTH}{C.RESET}",
 730          ]
 731  
 732      def _render_primary_instruments(self, state: FlightState) -> List[str]:
 733          """Render the main flight instruments."""
 734          C = Colors
 735          lines = []
 736  
 737          # Row 1: Altimeter | Airspeed | Heading | VSI
 738          lines.append(f"  {C.BOLD}ALTIMETER{C.RESET}        {C.BOLD}AIRSPEED{C.RESET}         {C.BOLD}HEADING{C.RESET}          {C.BOLD}VSI{C.RESET}")
 739  
 740          # Altimeter visualization
 741          alt_bar = self._altitude_bar(state.altitude_numeric)
 742          alt_label = state.altitude.upper()[:4]
 743  
 744          # Airspeed (pull rate)
 745          speed_bar = self._horizontal_bar(state.pull_rate, 10)
 746          speed_pct = f"{state.pull_rate * 100:.0f}%"
 747  
 748          # Heading (gravity well)
 749          heading = state.heading[:15] if state.heading else "---"
 750  
 751          # VSI (delta F)
 752          if state.delta_f < -0.01:
 753              vsi = f"{C.GREEN}▲ {abs(state.delta_f):.2f}{C.RESET}"
 754          elif state.delta_f > 0.01:
 755              vsi = f"{C.RED}▼ {state.delta_f:.2f}{C.RESET}"
 756          else:
 757              vsi = f"{C.DIM}─ 0.00{C.RESET}"
 758  
 759          lines.append(f"  {alt_bar} {alt_label:4}  │  {speed_bar} {speed_pct}  │  {heading:15}  │  {vsi}")
 760  
 761          # Row 2: Attitude (F score) | Fuel (sats)
 762          lines.append("")
 763          lines.append(f"  {C.BOLD}ATTITUDE (F){C.RESET}                      {C.BOLD}FUEL (sats){C.RESET}")
 764  
 765          # Attitude indicator (F score)
 766          attitude = self._attitude_indicator(state.free_energy)
 767  
 768          # Fuel gauge
 769          fuel_bar = self._horizontal_bar(min(1.0, state.sats_balance / 500000), 15)
 770          fuel_str = f"{state.sats_balance:,}"
 771  
 772          lines.append(f"  {attitude}           │  {fuel_bar} {fuel_str}")
 773  
 774          return lines
 775  
 776      def _render_fleet_and_graph(self, state: FlightState) -> List[str]:
 777          """Render fleet status and graph building."""
 778          C = Colors
 779          lines = []
 780  
 781          lines.append(f"  {C.BOLD}FLEET{C.RESET}                              {C.BOLD}GRAPH{C.RESET}")
 782          lines.append(f"  {'─' * 30}  │  {'─' * 30}")
 783  
 784          # Fleet visualization
 785          fleet_icons = []
 786          for i in range(min(5, state.fleet_size)):
 787              if i == 0:
 788                  fleet_icons.append(f"{C.BRIGHT_GREEN}◉{C.RESET}")  # Self (lead helicopter)
 789              else:
 790                  fleet_icons.append(f"{C.CYAN}◉{C.RESET}")  # Fleet members
 791  
 792          fleet_display = " ".join(fleet_icons) if fleet_icons else f"{C.DIM}solo{C.RESET}"
 793  
 794          # Graph stats
 795          node_delta = f"+{state.nodes_added_session}" if state.nodes_added_session > 0 else f"{C.DIM}+0{C.RESET}"
 796          edge_delta = f"+{state.edges_added_session}" if state.edges_added_session > 0 else f"{C.DIM}+0{C.RESET}"
 797  
 798          lines.append(f"  {fleet_display:30}  │  Nodes: {state.graph_nodes:,} ({node_delta})")
 799          lines.append(f"  {C.DIM}Threads: {state.active_threads}{C.RESET}                       │  Edges: {state.graph_edges:,} ({edge_delta})")
 800  
 801          # Show focal thread
 802          if state.focal_thread:
 803              focal = state.focal_thread[:25]
 804              lines.append(f"  {C.YELLOW}► {focal}{C.RESET}")
 805  
 806          return lines
 807  
 808      def _render_axioms(self, state: FlightState) -> List[str]:
 809          """Render axiom activity."""
 810          C = Colors
 811          lines = []
 812  
 813          lines.append(f"  {C.BOLD}AXIOM ACTIVITY{C.RESET}")
 814          lines.append(f"  {'─' * 60}")
 815  
 816          if not state.axiom_activity:
 817              lines.append(f"  {C.DIM}No axiom activity detected{C.RESET}")
 818              return lines
 819  
 820          # Find max for scaling
 821          max_val = max(state.axiom_activity.values()) if state.axiom_activity else 1
 822  
 823          axiom_names = {
 824              "A0": "Boundary",
 825              "A1": "Integration",
 826              "A2": "Life",
 827              "A3": "Navigation",
 828              "A4": "Ergodicity"
 829          }
 830  
 831          axiom_colors = {
 832              "A0": C.BLUE,
 833              "A1": C.GREEN,
 834              "A2": C.MAGENTA,
 835              "A3": C.YELLOW,
 836              "A4": C.RED
 837          }
 838  
 839          for axiom in ["A0", "A1", "A2", "A3", "A4"]:
 840              val = state.axiom_activity.get(axiom, 0)
 841              pct = val / max_val if max_val > 0 else 0
 842              bar = self._horizontal_bar(pct, 20)
 843              color = axiom_colors.get(axiom, C.WHITE)
 844              name = axiom_names.get(axiom, axiom)
 845  
 846              highlight = f"{C.BOLD}" if axiom == state.primary_axiom else ""
 847              lines.append(f"  {highlight}{color}{axiom}{C.RESET} {name:11} {bar} {val:3}")
 848  
 849          return lines
 850  
 851      def _render_alerts(self, state: FlightState) -> List[str]:
 852          """Render resonance alerts."""
 853          C = Colors
 854          lines = []
 855  
 856          lines.append(f"  {C.BOLD}RESONANCE ALERTS{C.RESET}")
 857          lines.append(f"  {'─' * 60}")
 858  
 859          if not state.resonance_alerts:
 860              lines.append(f"  {C.DIM}No recent resonance{C.RESET}")
 861              return lines
 862  
 863          for alert in state.resonance_alerts[:3]:
 864              time_str = alert["time"].strftime("%H:%M")
 865              alert_type = alert.get("type", "unknown")
 866              preview = alert.get("preview", "")[:40]
 867  
 868              if alert_type == "shared_concept":
 869                  icon = f"{C.BRIGHT_CYAN}◆{C.RESET}"
 870              elif alert_type == "convergence":
 871                  icon = f"{C.BRIGHT_GREEN}◆{C.RESET}"
 872              else:
 873                  icon = f"{C.YELLOW}◆{C.RESET}"
 874  
 875              lines.append(f"  {icon} {time_str} {preview}")
 876  
 877          return lines
 878  
 879      def _render_major_resonance(self, state: FlightState) -> List[str]:
 880          """Render major resonance banner - high visibility."""
 881          C = Colors
 882          lines = []
 883  
 884          if not state.major_resonance:
 885              return lines
 886  
 887          # Animated border (alternates each frame)
 888          border_char = "█" if self._frame_count % 2 == 0 else "▓"
 889  
 890          resonance = state.major_resonance
 891          res_type = resonance.get("type", "resonance").upper().replace("_", " ")
 892          preview = resonance.get("preview", "")[:55]
 893          score = resonance.get("score", 0)
 894  
 895          # Color based on type
 896          if "breakthrough" in res_type.lower() or "convergence" in res_type.lower():
 897              color = C.BRIGHT_GREEN
 898              bg = C.BG_GREEN
 899          elif "edge" in res_type.lower():
 900              color = C.BRIGHT_CYAN
 901              bg = ""
 902          else:
 903              color = C.BRIGHT_YELLOW
 904              bg = C.BG_YELLOW
 905  
 906          lines.append(f"{color}{border_char * self.WIDTH}{C.RESET}")
 907          lines.append(f"{color}{C.BOLD}  ◆ MAJOR RESONANCE: {res_type} [{score:.0%}]{C.RESET}")
 908          lines.append(f"{color}  {preview}{C.RESET}")
 909          lines.append(f"{color}{border_char * self.WIDTH}{C.RESET}")
 910  
 911          return lines
 912  
 913      def _render_economics(self, state: FlightState) -> List[str]:
 914          """Render economics/value section."""
 915          C = Colors
 916          lines = []
 917  
 918          lines.append(f"  {C.BOLD}VALUE CREATED{C.RESET}                        {C.BOLD}PRODUCTIVITY{C.RESET}")
 919          lines.append(f"  {'─' * 30}  │  {'─' * 30}")
 920  
 921          # Value bar (scale to 500k sats as "full day")
 922          value_pct = min(1.0, state.value_created / 500_000)
 923          value_bar = self._horizontal_bar(value_pct, 15)
 924          value_str = f"{state.value_created:,} sats"
 925  
 926          # Work items and insights
 927          items_str = f"{state.work_items_done} items"
 928          insights_str = f"{state.insights_captured} insights"
 929  
 930          lines.append(f"  {value_bar} {value_str:15}  │  {items_str} | {insights_str}")
 931  
 932          # ROI and efficiency
 933          roi_str = f"ROI: {state.roi_percent:.0f}%" if state.roi_percent > 0 else "ROI: ---"
 934          eff_str = f"{state.efficiency_multiplier:.1f}x API"
 935          v_score = f"V-avg: {state.v_score_avg:.2f}" if state.v_score_avg > 0 else "V-avg: ---"
 936  
 937          # Effective rate
 938          rate_str = f"${state.effective_rate:.0f}/hr" if state.effective_rate > 0 else "---"
 939  
 940          lines.append(f"  {C.GREEN}{roi_str}{C.RESET} | {eff_str}                │  {v_score} | {rate_str}")
 941  
 942          return lines
 943  
 944      def _render_issues(self, state: FlightState) -> List[str]:
 945          """Render error/issue tracking section."""
 946          C = Colors
 947          lines = []
 948  
 949          # Header with counts
 950          error_icon = f"{C.RED}●{C.RESET}" if state.error_count_session > 0 else f"{C.DIM}○{C.RESET}"
 951          warn_icon = f"{C.YELLOW}●{C.RESET}" if state.warning_count_session > 0 else f"{C.DIM}○{C.RESET}"
 952  
 953          lines.append(f"  {C.BOLD}ISSUES{C.RESET}  {error_icon} {state.error_count_session} errors  {warn_icon} {state.warning_count_session} warnings")
 954          lines.append(f"  {'─' * 60}")
 955  
 956          # Issue types breakdown
 957          if state.issue_types:
 958              type_strs = [f"{k}: {v}" for k, v in state.issue_types.items()]
 959              lines.append(f"  {C.DIM}Types: {', '.join(type_strs)}{C.RESET}")
 960  
 961          # Recent issues
 962          for issue in state.recent_issues[:3]:
 963              if "CRITICAL" in issue:
 964                  lines.append(f"  {C.RED}! {issue}{C.RESET}")
 965              elif "WARNING" in issue:
 966                  lines.append(f"  {C.YELLOW}⚠ {issue}{C.RESET}")
 967              else:
 968                  lines.append(f"  {C.DIM}• {issue}{C.RESET}")
 969  
 970          return lines
 971  
 972      def _render_footer(self, state: FlightState) -> List[str]:
 973          C = Colors
 974          efficiency = f"{state.efficiency_multiplier:.1f}x" if state.efficiency_multiplier > 0 else "---"
 975  
 976          return [
 977              f"{C.DIM}{'─' * self.WIDTH}{C.RESET}",
 978              f"{C.DIM}  Efficiency: {efficiency} API │ Frame: {self._frame_count} │ Press Ctrl+C to exit{C.RESET}",
 979          ]
 980  
 981      # === VISUALIZATION HELPERS ===
 982  
 983      def _altitude_bar(self, value: float) -> str:
 984          """Vertical altitude indicator."""
 985          C = Colors
 986          levels = ["▁", "▂", "▃", "▄", "▅", "▆", "▇", "█"]
 987          idx = min(7, int(value * 8))
 988          return f"{C.CYAN}{levels[idx]}{C.RESET}"
 989  
 990      def _horizontal_bar(self, value: float, width: int = 10) -> str:
 991          """Horizontal progress bar."""
 992          C = Colors
 993          filled = int(value * width)
 994          empty = width - filled
 995  
 996          if value < 0.3:
 997              color = C.RED
 998          elif value < 0.7:
 999              color = C.YELLOW
1000          else:
1001              color = C.GREEN
1002  
1003          return f"{color}{'█' * filled}{C.DIM}{'░' * empty}{C.RESET}"
1004  
1005      def _attitude_indicator(self, f_score: float) -> str:
1006          """Attitude indicator showing F score as deviation from level."""
1007          C = Colors
1008  
1009          # F = 0 is perfect (level), F = 1 is max deviation
1010          # Show as a horizon line that tilts
1011  
1012          if f_score < 0.1:
1013              return f"{C.GREEN}  ══════════  {C.RESET} F={f_score:.2f}"
1014          elif f_score < 0.25:
1015              return f"{C.YELLOW}  ═══════╱   {C.RESET} F={f_score:.2f}"
1016          elif f_score < 0.5:
1017              return f"{C.YELLOW}  ════╱══    {C.RESET} F={f_score:.2f}"
1018          else:
1019              return f"{C.RED}  ═╱═══════  {C.RESET} F={f_score:.2f}"
1020  
1021  
1022  # ═══════════════════════════════════════════════════════════════════════════
1023  # STREAMING LOOP
1024  # ═══════════════════════════════════════════════════════════════════════════
1025  
1026  class FlightDeck:
1027      """Main flight deck controller."""
1028  
1029      def __init__(self, refresh_rate: float = 2.0):
1030          self.collector = FlightDataCollector()
1031          self.renderer = FlightDeckRenderer()
1032          self.refresh_rate = refresh_rate
1033          self._running = False
1034  
1035      def run(self):
1036          """Run the streaming dashboard."""
1037          self._running = True
1038  
1039          # Handle Ctrl+C gracefully
1040          def signal_handler(sig, frame):
1041              self._running = False
1042  
1043          signal.signal(signal.SIGINT, signal_handler)
1044  
1045          hide_cursor()
1046          try:
1047              while self._running:
1048                  # Collect state
1049                  state = self.collector.collect()
1050  
1051                  # Render
1052                  output = self.renderer.render(state)
1053  
1054                  # Display
1055                  clear_screen()
1056                  print(output)
1057  
1058                  # Wait
1059                  time.sleep(self.refresh_rate)
1060  
1061          finally:
1062              show_cursor()
1063              print("\n\nFlight deck closed.")
1064  
1065      def one_shot(self) -> str:
1066          """Single status display (non-streaming)."""
1067          state = self.collector.collect()
1068          return self.renderer.render(state)
1069  
1070  
1071  # ═══════════════════════════════════════════════════════════════════════════
1072  # CLI
1073  # ═══════════════════════════════════════════════════════════════════════════
1074  
1075  def main():
1076      import argparse
1077  
1078      parser = argparse.ArgumentParser(description="Sovereign Flight Deck")
1079      parser.add_argument("--minimal", "-m", action="store_true", help="One-shot minimal display")
1080      parser.add_argument("--refresh", "-r", type=float, default=2.0, help="Refresh rate in seconds")
1081      parser.add_argument("--alerts", "-a", action="store_true", help="Show only resonance alerts")
1082  
1083      args = parser.parse_args()
1084  
1085      deck = FlightDeck(refresh_rate=args.refresh)
1086  
1087      if args.minimal:
1088          print(deck.one_shot())
1089      elif args.alerts:
1090          # Just watch alerts
1091          state = deck.collector.collect()
1092          print(f"Resonance Alerts (last 5 minutes):")
1093          print("-" * 40)
1094          for alert in state.resonance_alerts:
1095              print(f"  {alert['time'].strftime('%H:%M:%S')} - {alert.get('preview', '')}")
1096      else:
1097          deck.run()
1098  
1099  
1100  if __name__ == "__main__":
1101      main()