RFTSystems commited on
Commit
ccff55b
·
verified ·
1 Parent(s): 32cbb56

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +102 -531
app.py CHANGED
@@ -1,14 +1,13 @@
1
  import os
2
  import math
3
  import json
4
- import random
5
  import numpy as np
6
  import pandas as pd
7
  import matplotlib.pyplot as plt
8
  import gradio as gr
9
 
10
  # ===============================================================
11
- # Rendered Frame Theory (RFT) — Agent Console (All-in-One Space)
12
  # Author: Liam Grinstead
13
  # Purpose: Transparent, reproducible, benchmarkable agent demos
14
  # Dependencies: numpy, pandas, matplotlib, gradio (NO scipy)
@@ -21,9 +20,7 @@ os.makedirs(OUTDIR, exist_ok=True)
21
  # Shared utilities
22
  # -----------------------------
23
  def set_seed(seed: int):
24
- seed = int(seed) % (2**32 - 1)
25
- np.random.seed(seed)
26
- random.seed(seed)
27
 
28
  def clamp(x, lo, hi):
29
  return max(lo, min(hi, x))
@@ -40,7 +37,7 @@ def df_to_csv_file(df: pd.DataFrame, name: str):
40
  return path
41
 
42
  # -----------------------------
43
- # RFT Core: τ_eff + gating
44
  # -----------------------------
45
  def tau_eff_adaptive(
46
  uncertainty: float,
@@ -52,7 +49,7 @@ def tau_eff_adaptive(
52
  """
53
  τ_eff is implemented here as a timing/decision delay modifier.
54
  - base: baseline τ_eff
55
- - slow_by: explicit slow-down term
56
  - gain: reaction strength to uncertainty
57
  - cap: prevents absurd values
58
  """
@@ -65,7 +62,7 @@ def rft_confidence(uncertainty: float):
65
 
66
  def rft_gate(conf: float, tau_eff: float, threshold: float):
67
  """
68
- Collapse gate:
69
  - higher τ_eff makes the gate stricter
70
  - threshold is the minimum confidence needed
71
  """
@@ -167,7 +164,7 @@ def simulate_neo(
167
  ax = fig3.add_subplot(111)
168
  ax.step(df["t"], df["baseline_alert"], where="post")
169
  ax.step(df["t"], df["rft_alert"], where="post")
170
- ax.set_title("NEO: Alerts (Baseline vs RFT)")
171
  ax.set_xlabel("t (step)")
172
  ax.set_ylabel("alert (0/1)")
173
  p_alerts = save_plot(fig3, f"neo_alerts_seed{seed}.png")
@@ -265,7 +262,7 @@ def simulate_jitter(
265
  fig1 = plt.figure(figsize=(10, 4))
266
  ax = fig1.add_subplot(111)
267
  ax.plot(df["t"], df["jitter"])
268
- ax.set_title("Jitter: residual vs time (running RFT plant)")
269
  ax.set_xlabel("t (step)")
270
  ax.set_ylabel("jitter (arb)")
271
  p_jit = save_plot(fig1, f"jitter_residual_seed{seed}.png")
@@ -274,7 +271,7 @@ def simulate_jitter(
274
  ax = fig2.add_subplot(111)
275
  ax.step(df["t"], df["baseline_active"], where="post")
276
  ax.step(df["t"], df["rft_active"], where="post")
277
- ax.set_title("Jitter: Actuation duty (Baseline vs RFT gating)")
278
  ax.set_xlabel("t (step)")
279
  ax.set_ylabel("active (0/1)")
280
  p_duty = save_plot(fig2, f"jitter_duty_seed{seed}.png")
@@ -323,7 +320,6 @@ def simulate_landing(
323
  vv = -45.0
324
  x = 60.0
325
  xv = 0.0
326
-
327
  ix = 0.0
328
 
329
  anomalies = 0
@@ -332,11 +328,13 @@ def simulate_landing(
332
  rows = []
333
 
334
  g = -9.81
335
-
336
  LAT_CTRL = 0.95
337
  WIND_PUSH = 0.28
338
  VERT_CTRL = 0.22
339
 
 
 
 
340
  for t in range(int(steps)):
341
  gust = math.sin(0.08 * t) + 0.55 * math.sin(0.21 * t + 0.7)
342
  wind = (wind_max * 0.75) * gust + np.random.normal(0.0, 0.65)
@@ -360,6 +358,7 @@ def simulate_landing(
360
  anomaly_types.append("High lateral error near ground")
361
  if meas_alt < 150 and abs(meas_vv) > 15:
362
  anomaly_types.append("High descent rate near ground")
 
363
  is_anomaly = len(anomaly_types) > 0
364
  if is_anomaly:
365
  anomalies += 1
@@ -375,7 +374,9 @@ def simulate_landing(
375
  if meas_alt < 600:
376
  ix = clamp(ix + (meas_x * dt) * 0.0025, -40.0, 40.0)
377
 
378
- do_action = bool(rft_gate(conf, tau, gate_threshold))
 
 
379
 
380
  u_rft_x = 0.0
381
  u_rft_v = 0.0
@@ -473,375 +474,6 @@ def simulate_landing(
473
 
474
  return summary, [p_alt, p_x, p_w, p_a], csv_path
475
 
476
- # ===============================================================
477
- # Predator Avoidance (Reflex vs QuantumConscious "RFT-style")
478
- # ===============================================================
479
- def numpy_convolve2d_toroidal(array: np.ndarray, kernel: np.ndarray) -> np.ndarray:
480
- out = np.zeros_like(array, dtype=float)
481
- kcx = kernel.shape[0] // 2
482
- kcy = kernel.shape[1] // 2
483
- rows, cols = array.shape
484
- for i in range(rows):
485
- for j in range(cols):
486
- val = 0.0
487
- for m in range(kernel.shape[0]):
488
- for n in range(kernel.shape[1]):
489
- x = (i + m - kcx) % rows
490
- y = (j + n - kcy) % cols
491
- val += array[x, y] * kernel[m, n]
492
- out[i, j] = val
493
- return out
494
-
495
- class Predator:
496
- def __init__(self, grid_size: int):
497
- self.grid_size = grid_size
498
- self.x = random.randint(0, grid_size - 1)
499
- self.y = random.randint(0, grid_size - 1)
500
-
501
- def move(self):
502
- dx, dy = random.choice([(0,1), (0,-1), (1,0), (-1,0)])
503
- self.x = (self.x + dx) % self.grid_size
504
- self.y = (self.y + dy) % self.grid_size
505
-
506
- class ReflexAgent:
507
- def __init__(self, grid_size: int):
508
- self.grid_size = grid_size
509
- self.x = random.randint(0, grid_size - 1)
510
- self.y = random.randint(0, grid_size - 1)
511
- self.collisions = 0
512
-
513
- def move(self):
514
- dx, dy = random.choice([(0,1), (0,-1), (1,0), (-1,0)])
515
- self.x = (self.x + dx) % self.grid_size
516
- self.y = (self.y + dy) % self.grid_size
517
-
518
- class QuantumConsciousAgent:
519
- def __init__(
520
- self,
521
- grid_size: int,
522
- move_kernel: np.ndarray,
523
- energy_max: float,
524
- energy_regen: float,
525
- base_override_cost: float,
526
- quantum_boost_prob: float,
527
- quantum_boost_amount: float,
528
- sense_noise_prob: float,
529
- alpha: float,
530
- beta: float,
531
- dt_internal: float,
532
- override_threshold: float
533
- ):
534
- self.grid_size = grid_size
535
- self.move_kernel = move_kernel.astype(float)
536
-
537
- self.pos_prob = np.zeros((grid_size, grid_size), dtype=float)
538
- x, y = np.random.randint(grid_size), np.random.randint(grid_size)
539
- self.pos_prob[x, y] = 1.0
540
- self.x, self.y = int(x), int(y)
541
-
542
- self.energy_max = float(energy_max)
543
- self.energy = float(energy_max)
544
- self.energy_regen = float(energy_regen)
545
- self.base_override_cost = float(base_override_cost)
546
- self.quantum_boost_prob = float(quantum_boost_prob)
547
- self.quantum_boost_amount = float(quantum_boost_amount)
548
- self.sense_noise_prob = float(sense_noise_prob)
549
-
550
- self.alpha = float(alpha)
551
- self.beta = float(beta)
552
- self.dt_internal = float(dt_internal)
553
- self.override_threshold = float(override_threshold)
554
-
555
- # Start low so P_override is not pinned at the threshold.
556
- self.psi_override = (0.08 + 0j) # |psi|^2 = 0.0064
557
- self.overrides = 0
558
- self.collisions = 0
559
-
560
- def move(self):
561
- dx, dy = random.choice([(0,1), (0,-1), (1,0), (-1,0)])
562
- self.x = (self.x + dx) % self.grid_size
563
- self.y = (self.y + dy) % self.grid_size
564
-
565
- # Keep pos_prob consistent with actual state (otherwise threat stays meaningless)
566
- self.pos_prob.fill(0.0)
567
- self.pos_prob[self.x, self.y] = 1.0
568
-
569
- def sense_predators(self, predators):
570
- perceived = []
571
- for p in predators:
572
- if random.random() < self.sense_noise_prob:
573
- continue
574
- perceived.append((p.x, p.y))
575
- return perceived
576
-
577
- def compute_threat(self, perceived):
578
- threat = 0.0
579
- radius = 2
580
- for (px, py) in perceived:
581
- xs = [(px + dx) % self.grid_size for dx in range(-radius, radius + 1)]
582
- ys = [(py + dy) % self.grid_size for dy in range(-radius, radius + 1)]
583
- sub = self.pos_prob[np.ix_(xs, ys)]
584
- threat += float(sub.sum())
585
- return threat
586
-
587
- def update_override_state(self, perceived):
588
- """
589
- Make P_override responsive (amplitude changes), not phase-only.
590
- Threat pushes amplitude up; energy pushes it down.
591
- """
592
- T = self.compute_threat(perceived)
593
- E = self.energy / max(self.energy_max, 1e-9)
594
-
595
- drive = (self.alpha * T) - (self.beta * E)
596
-
597
- # amplitude pump/decay (bounded)
598
- exp_term = clamp(drive, -6.0, 6.0) * 0.22 * self.dt_internal
599
- amp = math.exp(exp_term)
600
- amp = clamp(amp, 0.75, 1.35)
601
-
602
- # keep a "quantum-style" phase evolution too
603
- H = drive + 0.01 * (abs(self.psi_override) ** 2)
604
- self.psi_override *= amp * np.exp(-1j * H * self.dt_internal)
605
-
606
- # cap magnitude so probability stays within [0,1]
607
- mag = abs(self.psi_override)
608
- if mag > 1.0:
609
- self.psi_override /= mag
610
-
611
- def get_override_probability(self):
612
- return float(min(abs(self.psi_override) ** 2, 1.0))
613
-
614
- def apply_override(self, perceived):
615
- field = numpy_convolve2d_toroidal(self.pos_prob, self.move_kernel)
616
- field = np.maximum(field, 0.0)
617
-
618
- for (px, py) in perceived:
619
- for dx in range(-2, 3):
620
- for dy in range(-2, 3):
621
- nx = (px + dx) % self.grid_size
622
- ny = (py + dy) % self.grid_size
623
- dist = abs(dx) + abs(dy)
624
- field[nx, ny] *= (1.0 - 0.30 / (dist + 1.0))
625
-
626
- s = float(field.sum())
627
- if s <= 0:
628
- field[:] = 1.0 / (self.grid_size * self.grid_size)
629
- else:
630
- field /= s
631
-
632
- self.pos_prob = field
633
-
634
- flat = self.pos_prob.flatten().copy()
635
- for (px, py) in perceived:
636
- flat[px * self.grid_size + py] = 0.0
637
-
638
- tot = float(flat.sum())
639
- if tot <= 0:
640
- self.move()
641
- return
642
-
643
- flat /= tot
644
- idx = np.random.choice(self.grid_size * self.grid_size, p=flat)
645
- self.x, self.y = divmod(int(idx), self.grid_size)
646
-
647
- def quantum_energy_boost(self):
648
- if random.random() < self.quantum_boost_prob:
649
- return float(self.quantum_boost_amount)
650
- return 0.0
651
-
652
- def regen_energy(self):
653
- boost = self.quantum_energy_boost()
654
- self.energy = clamp(self.energy + self.energy_regen + boost, 0.0, self.energy_max)
655
- if self.energy < self.energy_max and random.random() < 0.05:
656
- self.energy = self.energy_max
657
-
658
- def move_consciously(self, predators, group_coherence: float):
659
- if self.energy <= 0:
660
- self.move()
661
- return 0, 0.0, 0.0
662
-
663
- perceived = self.sense_predators(predators)
664
- self.update_override_state(perceived)
665
-
666
- P_ov = self.get_override_probability()
667
- threat = self.compute_threat(perceived)
668
-
669
- acted = 0
670
- if (P_ov >= self.override_threshold) and (self.energy > 0):
671
- effective_cost = self.base_override_cost * (1.0 - float(group_coherence))
672
- if self.energy >= effective_cost:
673
- self.overrides += 1
674
- self.energy -= effective_cost
675
- self.apply_override(perceived)
676
- self.psi_override = (0.08 + 0j) # reset after a collapse action
677
- acted = 1
678
- else:
679
- self.move()
680
- else:
681
- self.move()
682
-
683
- return acted, P_ov, threat
684
-
685
- def simulate_predator(
686
- seed: int,
687
- grid_size: int,
688
- steps: int,
689
- num_reflex: int,
690
- num_conscious: int,
691
- num_predators: int,
692
- group_coherence: float,
693
- sense_noise_prob: float,
694
- override_threshold: float,
695
- alpha: float,
696
- beta: float,
697
- dt_internal: float,
698
- energy_max: float,
699
- base_override_cost: float,
700
- energy_regen: float,
701
- quantum_boost_prob: float,
702
- quantum_boost_amount: float,
703
- show_heatmap: bool
704
- ):
705
- set_seed(seed)
706
-
707
- move_kernel = np.array([[0, 0.2, 0],
708
- [0.2, 0.2, 0.2],
709
- [0, 0.2, 0]], dtype=float)
710
-
711
- reflex_agents = [ReflexAgent(grid_size) for _ in range(int(num_reflex))]
712
- conscious_agents = [
713
- QuantumConsciousAgent(
714
- grid_size=grid_size,
715
- move_kernel=move_kernel,
716
- energy_max=energy_max,
717
- energy_regen=energy_regen,
718
- base_override_cost=base_override_cost,
719
- quantum_boost_prob=quantum_boost_prob,
720
- quantum_boost_amount=quantum_boost_amount,
721
- sense_noise_prob=sense_noise_prob,
722
- alpha=alpha,
723
- beta=beta,
724
- dt_internal=dt_internal,
725
- override_threshold=override_threshold
726
- )
727
- for _ in range(int(num_conscious))
728
- ]
729
- predators = [Predator(grid_size) for _ in range(int(num_predators))]
730
-
731
- rows = []
732
- ops_proxy = 0
733
-
734
- for t in range(int(steps)):
735
- for p in predators:
736
- p.move()
737
-
738
- for a in reflex_agents:
739
- a.move()
740
- for p in predators:
741
- if a.x == p.x and a.y == p.y:
742
- a.collisions += 1
743
-
744
- actions = []
745
- povs = []
746
- threats = []
747
- for a in conscious_agents:
748
- acted, P_ov, threat = a.move_consciously(predators, group_coherence)
749
- a.regen_energy()
750
- actions.append(acted)
751
- povs.append(P_ov)
752
- threats.append(threat)
753
- for p in predators:
754
- if a.x == p.x and a.y == p.y:
755
- a.collisions += 1
756
-
757
- ops_proxy += 18
758
-
759
- reflex_collisions = int(sum(a.collisions for a in reflex_agents))
760
- conscious_collisions = int(sum(a.collisions for a in conscious_agents))
761
- avg_overrides = float(np.mean([a.overrides for a in conscious_agents])) if conscious_agents else 0.0
762
- avg_energy = float(np.mean([a.energy for a in conscious_agents])) if conscious_agents else 0.0
763
- avg_threat = float(np.mean(threats)) if threats else 0.0
764
- avg_pov = float(np.mean(povs)) if povs else 0.0
765
- avg_act = float(np.mean(actions)) if actions else 0.0
766
-
767
- rows.append({
768
- "t": t,
769
- "reflex_collisions_cum": reflex_collisions,
770
- "conscious_collisions_cum": conscious_collisions,
771
- "avg_conscious_overrides": avg_overrides,
772
- "avg_conscious_energy": avg_energy,
773
- "avg_conscious_threat": avg_threat,
774
- "avg_conscious_P_override": avg_pov,
775
- "avg_conscious_action": avg_act,
776
- "predators_positions": "|".join([f"{p.x},{p.y}" for p in predators]),
777
- })
778
-
779
- df = pd.DataFrame(rows)
780
- csv_path = df_to_csv_file(df, f"predator_log_seed{seed}.csv")
781
-
782
- fig1 = plt.figure(figsize=(10, 4))
783
- ax = fig1.add_subplot(111)
784
- ax.plot(df["t"], df["reflex_collisions_cum"], label="Reflex collisions (cum)")
785
- ax.plot(df["t"], df["conscious_collisions_cum"], label="Conscious collisions (cum)")
786
- ax.set_title("Predator Avoidance: Collisions (Reflex vs RFT)")
787
- ax.set_xlabel("t (step)")
788
- ax.set_ylabel("collisions (cum)")
789
- ax.legend()
790
- p_col = save_plot(fig1, f"predator_collisions_seed{seed}.png")
791
-
792
- fig2 = plt.figure(figsize=(10, 4))
793
- ax = fig2.add_subplot(111)
794
- ax.plot(df["t"], df["avg_conscious_overrides"], label="Avg overrides (conscious)")
795
- ax.plot(df["t"], df["avg_conscious_energy"], label="Avg energy (conscious)")
796
- ax.set_title("Predator Avoidance: Overrides + Energy (Conscious)")
797
- ax.set_xlabel("t (step)")
798
- ax.set_ylabel("value")
799
- ax.legend()
800
- p_ov = save_plot(fig2, f"predator_overrides_energy_seed{seed}.png")
801
-
802
- fig3 = plt.figure(figsize=(10, 4))
803
- ax = fig3.add_subplot(111)
804
- ax.plot(df["t"], df["avg_conscious_threat"], label="Avg threat")
805
- ax.plot(df["t"], df["avg_conscious_P_override"], label="Avg P_override")
806
- ax.plot(df["t"], df["avg_conscious_action"], label="Avg action rate")
807
- ax.set_title("Predator Avoidance: Threat vs Override Probability vs Action Rate")
808
- ax.set_xlabel("t (step)")
809
- ax.set_ylabel("value")
810
- ax.legend()
811
- p_thr = save_plot(fig3, f"predator_threat_seed{seed}.png")
812
-
813
- heatmap_path = None
814
- if show_heatmap and len(conscious_agents) > 0:
815
- field = conscious_agents[0].pos_prob
816
- fig4 = plt.figure(figsize=(6, 5))
817
- ax = fig4.add_subplot(111)
818
- im = ax.imshow(field, aspect="auto")
819
- ax.set_title("Conscious Agent[0]: Final probability field (pos_prob)")
820
- ax.set_xlabel("y")
821
- ax.set_ylabel("x")
822
- fig4.colorbar(im, ax=ax, fraction=0.046, pad=0.04)
823
- heatmap_path = save_plot(fig4, f"predator_probfield_seed{seed}.png")
824
-
825
- summary = {
826
- "seed": int(seed),
827
- "grid_size": int(grid_size),
828
- "steps": int(steps),
829
- "num_reflex": int(num_reflex),
830
- "num_conscious": int(num_conscious),
831
- "num_predators": int(num_predators),
832
- "final_reflex_collisions": int(df["reflex_collisions_cum"].iloc[-1]) if len(df) else 0,
833
- "final_conscious_collisions": int(df["conscious_collisions_cum"].iloc[-1]) if len(df) else 0,
834
- "final_avg_conscious_overrides": float(df["avg_conscious_overrides"].iloc[-1]) if len(df) else 0.0,
835
- "final_avg_conscious_energy": float(df["avg_conscious_energy"].iloc[-1]) if len(df) else 0.0,
836
- "ops_proxy": int(ops_proxy),
837
- }
838
-
839
- imgs = [p_col, p_ov, p_thr]
840
- if heatmap_path is not None:
841
- imgs.append(heatmap_path)
842
-
843
- return summary, imgs, csv_path
844
-
845
  # -----------------------------
846
  # Benchmarks
847
  # -----------------------------
@@ -929,37 +561,41 @@ def run_benchmarks(
929
  f"- Landing: final offset={l_sum['final_landing_offset_m']:.2f} m (goal 10 m), anomalies={l_sum['total_anomalies_detected']}, actions={l_sum['total_control_actions']}\n"
930
  )
931
 
932
- all_imgs = neo_imgs + jit_imgs + land_imgs
933
  return txt, score, score_path, all_imgs, [neo_csv, jit_csv, land_csv]
934
 
935
  # -----------------------------
936
  # UI text blocks
937
  # -----------------------------
938
  HOME_MD = """
939
- # Rendered Frame Theory (RFT) — Agent Console
940
 
941
- This Space is meant to be transparent, reproducible, and benchmarkable.
 
 
 
942
 
943
  Run it. Change parameters. Break it. Compare baseline vs RFT.
944
 
945
- Core idea:
946
 
947
  **Decision timing matters.**
948
- RFT treats timing (τ_eff), uncertainty, and action “collapse” as first-class controls.
 
 
 
 
 
949
 
950
- This Space contains:
951
- - **NEO alerting**
952
- - **Satellite jitter reduction**
953
- - **Starship-style landing harness**
954
- - **Predator avoidance** (Reflex vs RFT-style "QuantumConscious" agents)
955
 
956
- No SciPy. No hidden dependencies. No model weights.
957
  """
958
 
959
  LIVE_MD = """
960
  # Live Console
961
 
962
- Run everything quickly and export logs.
963
 
964
  - deterministic runs (seeded)
965
  - plots saved
@@ -968,80 +604,101 @@ Run everything quickly and export logs.
968
  """
969
 
970
  THEORY_PRACTICE_MD = """
971
- # Theory → Practice (how I implement RFT here)
972
 
973
- ## 1) Uncertainty
974
- Explicit uncertainty proxy from noise + disturbance scale.
 
 
975
 
976
  ## 2) Confidence
977
- confidence = 1 − uncertainty (clipped 0..1).
978
 
979
  ## 3) Adaptive τ_eff
980
- Higher uncertainty higher τ_eff.
 
 
981
 
982
- ## 4) Collapse gate
983
- Act only when the gate passes:
984
- - confidence exceeds a threshold
985
- - τ_eff increases strictness under uncertainty
986
 
987
- ## 5) Why it matters
988
  Baseline controllers often act constantly.
989
- RFT tries to act less often, but more decisively.
990
  """
991
 
992
  MATH_MD = r"""
993
  # Mathematics (minimal and implementation-linked)
994
 
995
- u [0,1] : uncertainty proxy
996
- C ∈ [0,1] : confidence proxy
997
- τ_eff 1 : effective decision timing factor
 
 
 
998
 
999
- Confidence:
1000
  \[
1001
  C = \text{clip}(1 - u, 0, 1)
1002
  \]
1003
 
1004
- Adaptive τ_eff:
1005
  \[
1006
  \tau_{\text{eff}} = \text{clip}(1 + 1.0 + g\cdot u,\; 1,\; \tau_{\max})
1007
  \]
1008
 
1009
- Collapse gate (concept):
 
1010
  \[
1011
  \text{Gate} = \left[C \ge \theta + k(\tau_{\text{eff}}-1)\right]
1012
  \]
 
 
1013
  """
1014
 
1015
  INVESTOR_MD = """
1016
- # Investor / Agency Walkthrough
1017
 
1018
- What this Space demonstrates:
1019
- - alert filtering (NEO)
 
1020
  - stabilisation (jitter reduction)
1021
- - anomaly-aware control (landing harness)
1022
- - threat-aware avoidance (predator demo)
1023
 
1024
- What it is not:
1025
- - not flight-certified
1026
- - not a production pipeline
1027
- - not a claim that anyone is using it
 
1028
 
1029
- What makes it production-grade:
 
 
 
 
 
1030
  - real sensor ingestion + timing constraints
1031
  - hardware-in-loop testing
1032
- - dataset validation
 
1033
  """
1034
 
1035
  REPRO_MD = """
1036
  # Reproducibility & Logs
1037
 
1038
- Everything is reproducible:
1039
- - set seed
1040
- - run
1041
- - export CSV
1042
- - verify plots + metrics
1043
-
1044
- CSV schema is explicit in the exports.
 
 
 
 
1045
  """
1046
 
1047
  # -----------------------------
@@ -1091,39 +748,6 @@ def ui_run_landing(seed, steps, dt, wind_max, thrust_noise, kp_base, kp_rft, gat
1091
  summary_txt = json.dumps(summary, indent=2)
1092
  return summary_txt, imgs[0], imgs[1], imgs[2], imgs[3], csv_path
1093
 
1094
- def ui_run_predator(seed, grid_size, steps, num_reflex, num_conscious, num_predators,
1095
- group_coherence, sense_noise_prob, override_threshold,
1096
- alpha, beta, dt_internal,
1097
- energy_max, base_override_cost, energy_regen,
1098
- quantum_boost_prob, quantum_boost_amount,
1099
- show_heatmap):
1100
- summary, imgs, csv_path = simulate_predator(
1101
- seed=int(seed),
1102
- grid_size=int(grid_size),
1103
- steps=int(steps),
1104
- num_reflex=int(num_reflex),
1105
- num_conscious=int(num_conscious),
1106
- num_predators=int(num_predators),
1107
- group_coherence=float(group_coherence),
1108
- sense_noise_prob=float(sense_noise_prob),
1109
- override_threshold=float(override_threshold),
1110
- alpha=float(alpha),
1111
- beta=float(beta),
1112
- dt_internal=float(dt_internal),
1113
- energy_max=float(energy_max),
1114
- base_override_cost=float(base_override_cost),
1115
- energy_regen=float(energy_regen),
1116
- quantum_boost_prob=float(quantum_boost_prob),
1117
- quantum_boost_amount=float(quantum_boost_amount),
1118
- show_heatmap=bool(show_heatmap)
1119
- )
1120
- summary_txt = json.dumps(summary, indent=2)
1121
- img1 = imgs[0] if len(imgs) > 0 else None
1122
- img2 = imgs[1] if len(imgs) > 1 else None
1123
- img3 = imgs[2] if len(imgs) > 2 else None
1124
- img4 = imgs[3] if len(imgs) > 3 else None
1125
- return summary_txt, img1, img2, img3, img4, csv_path
1126
-
1127
  def ui_run_bench(seed, neo_steps, neo_dt, neo_alert_km, neo_noise_km, jit_steps, jit_dt, jit_noise, land_steps, land_dt, land_wind, land_thrust_noise, tau_gain):
1128
  txt, score_df, score_csv, imgs, logs = run_benchmarks(
1129
  seed=int(seed),
@@ -1141,7 +765,7 @@ def ui_run_bench(seed, neo_steps, neo_dt, neo_alert_km, neo_noise_km, jit_steps,
1141
  # -----------------------------
1142
  # Gradio UI
1143
  # -----------------------------
1144
- with gr.Blocks(title="RFT — Agent Console (NEO / Jitter / Landing / Predator)") as demo:
1145
  gr.Markdown(HOME_MD)
1146
 
1147
  with gr.Tabs():
@@ -1170,7 +794,7 @@ with gr.Blocks(title="RFT — Agent Console (NEO / Jitter / Landing / Predator)"
1170
  land_wind = gr.Slider(0.0, 25.0, value=15.0, step=0.5, label="Landing wind max (m/s)")
1171
  land_thrust_noise = gr.Slider(0.0, 10.0, value=3.0, step=0.1, label="Landing thrust noise")
1172
 
1173
- run_b = gr.Button("Run Full Benchmarks (Baseline vs RFT)")
1174
 
1175
  bench_txt = gr.Textbox(label="Benchmark summary", lines=6)
1176
  bench_table = gr.Dataframe(label="Scorecard (CSV also exported)")
@@ -1204,11 +828,12 @@ with gr.Blocks(title="RFT — Agent Console (NEO / Jitter / Landing / Predator)"
1204
  ]
1205
  )
1206
 
1207
- with gr.Tab("NEO Agent"):
1208
  gr.Markdown(
1209
- "# Near-Earth Object (NEO) Alerting Agent\n"
 
1210
  "Baseline: distance threshold only.\n"
1211
- "RFT: distance threshold + confidence + τ_eff collapse gate.\n"
1212
  )
1213
  with gr.Row():
1214
  seed_neo = gr.Number(value=42, precision=0, label="Seed")
@@ -1217,7 +842,7 @@ with gr.Blocks(title="RFT — Agent Console (NEO / Jitter / Landing / Predator)"
1217
  with gr.Row():
1218
  alert_km = gr.Slider(1000, 20000, value=5000, step=50, label="Alert threshold (km)")
1219
  noise_km = gr.Slider(0.0, 200.0, value=35.0, step=1.0, label="Measurement noise (km)")
1220
- rft_conf_th = gr.Slider(0.1, 0.95, value=0.55, step=0.01, label="RFT confidence threshold")
1221
  tau_gain = gr.Slider(0.0, 3.0, value=1.2, step=0.05, label="τ_eff gain")
1222
  show_debug = gr.Checkbox(value=False, label="Show debug table (first rows)")
1223
  run_neo = gr.Button("Run NEO Simulation")
@@ -1236,11 +861,11 @@ with gr.Blocks(title="RFT — Agent Console (NEO / Jitter / Landing / Predator)"
1236
  outputs=[out_neo_summary, out_neo_debug, out_neo_img1, out_neo_img2, out_neo_img3, out_neo_csv]
1237
  )
1238
 
1239
- with gr.Tab("Satellite Jitter Agent"):
1240
  gr.Markdown(
1241
- "# Satellite Jitter Reduction\n"
1242
  "Baseline: continuous correction.\n"
1243
- "RFT: gated correction using confidence + τ_eff.\n"
1244
  )
1245
  with gr.Row():
1246
  seed_j = gr.Number(value=42, precision=0, label="Seed")
@@ -1269,7 +894,7 @@ with gr.Blocks(title="RFT — Agent Console (NEO / Jitter / Landing / Predator)"
1269
 
1270
  with gr.Tab("Starship Landing Harness"):
1271
  gr.Markdown(
1272
- "# Starship-style Landing Harness (Simplified)\n"
1273
  "This is not a flight model. It’s a timing-control harness.\n"
1274
  )
1275
  with gr.Row():
@@ -1301,65 +926,11 @@ with gr.Blocks(title="RFT — Agent Console (NEO / Jitter / Landing / Predator)"
1301
  outputs=[out_l_summary, out_l_img1, out_l_img2, out_l_img3, out_l_img4, out_l_csv]
1302
  )
1303
 
1304
- with gr.Tab("Predator Avoidance"):
1305
  gr.Markdown(
1306
- "# Predator Avoidance (Reflex vs RFT)\n"
1307
- "Grid world with roaming predators.\n"
1308
- "Reflex agents: random walk.\n"
1309
- "Conscious agents: probability field + threat-weighted override.\n"
1310
- )
1311
-
1312
- with gr.Row():
1313
- seed_p = gr.Number(value=42, precision=0, label="Seed")
1314
- grid_size = gr.Slider(10, 60, value=20, step=1, label="Grid size")
1315
- steps_p = gr.Slider(50, 1500, value=200, step=1, label="Steps")
1316
-
1317
- with gr.Row():
1318
- num_reflex = gr.Slider(0, 50, value=10, step=1, label="Reflex agents")
1319
- num_conscious = gr.Slider(0, 20, value=3, step=1, label="Conscious agents")
1320
- num_predators = gr.Slider(1, 20, value=3, step=1, label="Predators")
1321
-
1322
- with gr.Accordion("RFT / Agent parameters", open=True):
1323
- with gr.Row():
1324
- group_coherence = gr.Slider(0.0, 0.95, value=0.30, step=0.01, label="Group coherence")
1325
- sense_noise_prob = gr.Slider(0.0, 0.9, value=0.10, step=0.01, label="Sense noise probability")
1326
- override_threshold = gr.Slider(0.0, 1.0, value=0.02, step=0.005, label="Override threshold (P_ov)")
1327
-
1328
- with gr.Row():
1329
- alpha = gr.Slider(0.0, 50.0, value=15.0, step=0.5, label="alpha (threat gain)")
1330
- beta = gr.Slider(0.0, 10.0, value=0.5, step=0.05, label="beta (energy term)")
1331
- dt_internal = gr.Slider(0.01, 1.0, value=0.2, step=0.01, label="override dt")
1332
-
1333
- with gr.Row():
1334
- energy_max = gr.Slider(1.0, 300.0, value=100.0, step=1.0, label="Energy max")
1335
- base_override_cost = gr.Slider(0.0, 10.0, value=1.0, step=0.1, label="Base override cost")
1336
- energy_regen = gr.Slider(0.0, 1.0, value=0.05, step=0.01, label="Energy regen")
1337
-
1338
- with gr.Row():
1339
- quantum_boost_prob = gr.Slider(0.0, 1.0, value=0.10, step=0.01, label="Quantum boost probability")
1340
- quantum_boost_amount = gr.Slider(0.0, 50.0, value=5.0, step=0.5, label="Quantum boost amount")
1341
- show_heatmap = gr.Checkbox(value=True, label="Show probability field heatmap (agent[0])")
1342
-
1343
- run_p = gr.Button("Run Predator Simulation")
1344
-
1345
- out_p_summary = gr.Textbox(label="Summary JSON", lines=12)
1346
- with gr.Row():
1347
- out_p_img1 = gr.Image(label="Collisions (cumulative)")
1348
- out_p_img2 = gr.Image(label="Overrides + Energy")
1349
- with gr.Row():
1350
- out_p_img3 = gr.Image(label="Threat / P_override / Action rate")
1351
- out_p_img4 = gr.Image(label="Final probability field (optional)")
1352
- out_p_csv = gr.File(label="Download Predator CSV log")
1353
-
1354
- run_p.click(
1355
- ui_run_predator,
1356
- inputs=[seed_p, grid_size, steps_p, num_reflex, num_conscious, num_predators,
1357
- group_coherence, sense_noise_prob, override_threshold,
1358
- alpha, beta, dt_internal,
1359
- energy_max, base_override_cost, energy_regen,
1360
- quantum_boost_prob, quantum_boost_amount,
1361
- show_heatmap],
1362
- outputs=[out_p_summary, out_p_img1, out_p_img2, out_p_img3, out_p_img4, out_p_csv]
1363
  )
1364
 
1365
  with gr.Tab("Theory → Practice"):
 
1
  import os
2
  import math
3
  import json
 
4
  import numpy as np
5
  import pandas as pd
6
  import matplotlib.pyplot as plt
7
  import gradio as gr
8
 
9
  # ===============================================================
10
+ # Rendered Frame Theory (RFT) — Observer Agent Console (All-in-One Space)
11
  # Author: Liam Grinstead
12
  # Purpose: Transparent, reproducible, benchmarkable agent demos
13
  # Dependencies: numpy, pandas, matplotlib, gradio (NO scipy)
 
20
  # Shared utilities
21
  # -----------------------------
22
  def set_seed(seed: int):
23
+ np.random.seed(int(seed) % (2**32 - 1))
 
 
24
 
25
  def clamp(x, lo, hi):
26
  return max(lo, min(hi, x))
 
37
  return path
38
 
39
  # -----------------------------
40
+ # RFT Core: τ_eff + gating (Observer-style decision timing)
41
  # -----------------------------
42
  def tau_eff_adaptive(
43
  uncertainty: float,
 
49
  """
50
  τ_eff is implemented here as a timing/decision delay modifier.
51
  - base: baseline τ_eff
52
+ - slow_by: explicit slow-down term (I wanted this behaviour: slow by 1.0)
53
  - gain: reaction strength to uncertainty
54
  - cap: prevents absurd values
55
  """
 
62
 
63
  def rft_gate(conf: float, tau_eff: float, threshold: float):
64
  """
65
+ Decision gate (observer-style “commit” trigger):
66
  - higher τ_eff makes the gate stricter
67
  - threshold is the minimum confidence needed
68
  """
 
164
  ax = fig3.add_subplot(111)
165
  ax.step(df["t"], df["baseline_alert"], where="post")
166
  ax.step(df["t"], df["rft_alert"], where="post")
167
+ ax.set_title("NEO: Alerts (Baseline vs Observer-gated RFT)")
168
  ax.set_xlabel("t (step)")
169
  ax.set_ylabel("alert (0/1)")
170
  p_alerts = save_plot(fig3, f"neo_alerts_seed{seed}.png")
 
262
  fig1 = plt.figure(figsize=(10, 4))
263
  ax = fig1.add_subplot(111)
264
  ax.plot(df["t"], df["jitter"])
265
+ ax.set_title("Jitter: residual vs time (running observer-gated plant)")
266
  ax.set_xlabel("t (step)")
267
  ax.set_ylabel("jitter (arb)")
268
  p_jit = save_plot(fig1, f"jitter_residual_seed{seed}.png")
 
271
  ax = fig2.add_subplot(111)
272
  ax.step(df["t"], df["baseline_active"], where="post")
273
  ax.step(df["t"], df["rft_active"], where="post")
274
+ ax.set_title("Jitter: Actuation duty (Baseline vs Observer-gated RFT)")
275
  ax.set_xlabel("t (step)")
276
  ax.set_ylabel("active (0/1)")
277
  p_duty = save_plot(fig2, f"jitter_duty_seed{seed}.png")
 
320
  vv = -45.0
321
  x = 60.0
322
  xv = 0.0
 
323
  ix = 0.0
324
 
325
  anomalies = 0
 
328
  rows = []
329
 
330
  g = -9.81
 
331
  LAT_CTRL = 0.95
332
  WIND_PUSH = 0.28
333
  VERT_CTRL = 0.22
334
 
335
+ OVERRIDE_X = 18.0
336
+ OVERRIDE_ALT = 260.0
337
+
338
  for t in range(int(steps)):
339
  gust = math.sin(0.08 * t) + 0.55 * math.sin(0.21 * t + 0.7)
340
  wind = (wind_max * 0.75) * gust + np.random.normal(0.0, 0.65)
 
358
  anomaly_types.append("High lateral error near ground")
359
  if meas_alt < 150 and abs(meas_vv) > 15:
360
  anomaly_types.append("High descent rate near ground")
361
+
362
  is_anomaly = len(anomaly_types) > 0
363
  if is_anomaly:
364
  anomalies += 1
 
374
  if meas_alt < 600:
375
  ix = clamp(ix + (meas_x * dt) * 0.0025, -40.0, 40.0)
376
 
377
+ do_action = rft_gate(conf, tau, gate_threshold)
378
+ must_act = (abs(meas_x) > OVERRIDE_X) or (meas_alt < OVERRIDE_ALT)
379
+ do_action = bool(do_action or must_act)
380
 
381
  u_rft_x = 0.0
382
  u_rft_v = 0.0
 
474
 
475
  return summary, [p_alt, p_x, p_w, p_a], csv_path
476
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
477
  # -----------------------------
478
  # Benchmarks
479
  # -----------------------------
 
561
  f"- Landing: final offset={l_sum['final_landing_offset_m']:.2f} m (goal 10 m), anomalies={l_sum['total_anomalies_detected']}, actions={l_sum['total_control_actions']}\n"
562
  )
563
 
564
+ all_imgs = neo_imgs + jit_imgs + land_imgs # 3 + 3 + 4 = 10
565
  return txt, score, score_path, all_imgs, [neo_csv, jit_csv, land_csv]
566
 
567
  # -----------------------------
568
  # UI text blocks
569
  # -----------------------------
570
  HOME_MD = """
571
+ # RFT — Observer Agent Console
572
 
573
+ I built this Space to be transparent, reproducible, and benchmarkable.
574
+
575
+ This is **not** a consciousness claim.
576
+ When I say “observer” here, I mean a practical decision-timing mechanism: uncertainty → τ_eff → gate → commit or wait.
577
 
578
  Run it. Change parameters. Break it. Compare baseline vs RFT.
579
 
580
+ What I’m demonstrating is a simple idea:
581
 
582
  **Decision timing matters.**
583
+ RFT treats timing (τ_eff), uncertainty, and action “commit” as first-class controls.
584
+
585
+ This Space contains three working agent harnesses:
586
+ - **NEO alerting** (filter noisy close-approach alerts)
587
+ - **Satellite jitter reduction** (reduce actuator duty / chatter while keeping residual low)
588
+ - **Starship-style landing harness** (simplified, but structured to test decision timing under wind/thrust disturbances)
589
 
590
+ Every tab shows what it’s doing, why, and where it wins or loses.
 
 
 
 
591
 
592
+ No SciPy. No hidden dependencies. No model weights. No tricks.
593
  """
594
 
595
  LIVE_MD = """
596
  # Live Console
597
 
598
+ This tab is a single place to run everything quickly and export logs.
599
 
600
  - deterministic runs (seeded)
601
  - plots saved
 
604
  """
605
 
606
  THEORY_PRACTICE_MD = """
607
+ # Theory → Practice (how I implement the RFT Observer Agent idea here)
608
 
609
+ This Space uses RFT in a practical way:
610
+
611
+ ## 1) Uncertainty (explicit)
612
+ I compute an uncertainty proxy from noise + disturbance scale.
613
 
614
  ## 2) Confidence
615
+ Confidence is the complement: confidence = 1 − uncertainty (clipped 0..1).
616
 
617
  ## 3) Adaptive τ_eff
618
+ τ_eff is implemented as a timing/decision strictness modifier:
619
+ - higher uncertainty → higher τ_eff
620
+ - and yes, I explicitly slow τ_eff by 1.0, because this was the behaviour I wanted to test.
621
 
622
+ ## 4) Decision gate
623
+ I only apply “decisive actions” when the gate condition passes:
624
+ - confidence must exceed a threshold
625
+ - τ_eff increases strictness (makes the gate harder under uncertainty)
626
 
627
+ ## 5) Why this matters
628
  Baseline controllers often act constantly.
629
+ This observer-gated approach tries to act less often, but more decisively, so you waste less energy and trigger fewer junk corrections/alerts.
630
  """
631
 
632
  MATH_MD = r"""
633
  # Mathematics (minimal and implementation-linked)
634
 
635
+ ## Variables (used in this Space)
636
+ - u ∈ [0,1] : uncertainty proxy (dimensionless)
637
+ - C ∈ [0,1] : confidence proxy (dimensionless)
638
+ - τ_eff ≥ 1 : effective decision-timing factor (dimensionless)
639
+
640
+ ## Definitions
641
 
642
+ ### Confidence
643
  \[
644
  C = \text{clip}(1 - u, 0, 1)
645
  \]
646
 
647
+ ### Adaptive τ_eff (with “slow by 1.0”)
648
  \[
649
  \tau_{\text{eff}} = \text{clip}(1 + 1.0 + g\cdot u,\; 1,\; \tau_{\max})
650
  \]
651
 
652
+ ### Decision gate (concept)
653
+ Higher τ_eff makes decisions stricter:
654
  \[
655
  \text{Gate} = \left[C \ge \theta + k(\tau_{\text{eff}}-1)\right]
656
  \]
657
+
658
+ That is exactly what I implement here: more uncertainty → higher τ_eff → harder gate → fewer low-confidence actions.
659
  """
660
 
661
  INVESTOR_MD = """
662
+ # Investor / Agency Walkthrough (plain language)
663
 
664
+ ## What I’m proving inside this Space
665
+ I’m demonstrating a decision-timing framework that can be applied to:
666
+ - alert filtering (NEO / tracking)
667
  - stabilisation (jitter reduction)
668
+ - anomaly-aware control loops (landing harness)
 
669
 
670
+ This is a runnable harness:
671
+ - you can reproduce results with seeds
672
+ - you can export logs
673
+ - you can compare baseline vs RFT
674
+ - you can change thresholds and see behaviour shift
675
 
676
+ ## What I’m not claiming
677
+ - I’m not claiming flight certification
678
+ - I’m not claiming any company is using this
679
+ - I’m not claiming this replaces aerospace validation pipelines
680
+
681
+ ## What would make it production-grade
682
  - real sensor ingestion + timing constraints
683
  - hardware-in-loop testing
684
+ - systematic dataset validation
685
+ - integration targets (embedded, REST, batch)
686
  """
687
 
688
  REPRO_MD = """
689
  # Reproducibility & Logs
690
 
691
+ Everything here is reproducible:
692
+ - set the seed
693
+ - run baseline vs RFT with the same seed
694
+ - export the CSV
695
+ - verify plots and metrics
696
+
697
+ CSV schema is explicit in the exports:
698
+ - time index
699
+ - state values
700
+ - uncertainty, confidence, τ_eff
701
+ - alerts/actions flags
702
  """
703
 
704
  # -----------------------------
 
748
  summary_txt = json.dumps(summary, indent=2)
749
  return summary_txt, imgs[0], imgs[1], imgs[2], imgs[3], csv_path
750
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
751
  def ui_run_bench(seed, neo_steps, neo_dt, neo_alert_km, neo_noise_km, jit_steps, jit_dt, jit_noise, land_steps, land_dt, land_wind, land_thrust_noise, tau_gain):
752
  txt, score_df, score_csv, imgs, logs = run_benchmarks(
753
  seed=int(seed),
 
765
  # -----------------------------
766
  # Gradio UI
767
  # -----------------------------
768
+ with gr.Blocks(title="RFT — Observer Agent Console (NEO / Jitter / Landing)") as demo:
769
  gr.Markdown(HOME_MD)
770
 
771
  with gr.Tabs():
 
794
  land_wind = gr.Slider(0.0, 25.0, value=15.0, step=0.5, label="Landing wind max (m/s)")
795
  land_thrust_noise = gr.Slider(0.0, 10.0, value=3.0, step=0.1, label="Landing thrust noise")
796
 
797
+ run_b = gr.Button("Run Full Benchmarks (Baseline vs Observer-gated RFT)")
798
 
799
  bench_txt = gr.Textbox(label="Benchmark summary", lines=6)
800
  bench_table = gr.Dataframe(label="Scorecard (CSV also exported)")
 
828
  ]
829
  )
830
 
831
+ with gr.Tab("NEO Observer Agent"):
832
  gr.Markdown(
833
+ "# Near-Earth Object (NEO) Observer Agent\n"
834
+ "This is a test harness for filtering close-approach alerts under noise.\n"
835
  "Baseline: distance threshold only.\n"
836
+ "Observer-gated RFT: distance threshold + confidence + τ_eff decision gate.\n"
837
  )
838
  with gr.Row():
839
  seed_neo = gr.Number(value=42, precision=0, label="Seed")
 
842
  with gr.Row():
843
  alert_km = gr.Slider(1000, 20000, value=5000, step=50, label="Alert threshold (km)")
844
  noise_km = gr.Slider(0.0, 200.0, value=35.0, step=1.0, label="Measurement noise (km)")
845
+ rft_conf_th = gr.Slider(0.1, 0.95, value=0.55, step=0.01, label="Confidence threshold")
846
  tau_gain = gr.Slider(0.0, 3.0, value=1.2, step=0.05, label="τ_eff gain")
847
  show_debug = gr.Checkbox(value=False, label="Show debug table (first rows)")
848
  run_neo = gr.Button("Run NEO Simulation")
 
861
  outputs=[out_neo_summary, out_neo_debug, out_neo_img1, out_neo_img2, out_neo_img3, out_neo_csv]
862
  )
863
 
864
+ with gr.Tab("Satellite Jitter Observer Agent"):
865
  gr.Markdown(
866
+ "# Satellite Jitter Reduction (Observer-gated)\n"
867
  "Baseline: continuous correction.\n"
868
+ "Observer-gated RFT: gated correction using confidence + τ_eff.\n"
869
  )
870
  with gr.Row():
871
  seed_j = gr.Number(value=42, precision=0, label="Seed")
 
894
 
895
  with gr.Tab("Starship Landing Harness"):
896
  gr.Markdown(
897
+ "# Starship-style Landing Harness (Observer-gated decision timing)\n"
898
  "This is not a flight model. It’s a timing-control harness.\n"
899
  )
900
  with gr.Row():
 
926
  outputs=[out_l_summary, out_l_img1, out_l_img2, out_l_img3, out_l_img4, out_l_csv]
927
  )
928
 
929
+ with gr.Tab("Benchmarks"):
930
  gr.Markdown(
931
+ "# Benchmarks\n"
932
+ "Run full packs from the Live Console tab.\n"
933
+ "Everything is seeded, logged, and exportable.\n"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
934
  )
935
 
936
  with gr.Tab("Theory → Practice"):