mirror of
https://github.com/theniceboy/.config.git
synced 2026-07-16 22:01:21 +08:00
tracker + 2nd tmux status line
This commit is contained in:
parent
f667ba7225
commit
91deb129a8
22 changed files with 2369 additions and 657 deletions
194
agent-tracker/test/docker/test_alt_u_e_sibling.py
Normal file
194
agent-tracker/test/docker/test_alt_u_e_sibling.py
Normal file
|
|
@ -0,0 +1,194 @@
|
|||
#!/usr/bin/env python3
|
||||
"""Alt-U / Alt-E sibling reordering in the goals panel.
|
||||
|
||||
Covers the confirmed scenarios:
|
||||
1. Alt-E swaps a goal down among siblings; cursor follows.
|
||||
2. Boundary: Alt-E on the last sibling is a no-op.
|
||||
3. Alt-U swaps up (does NOT nest, unlike m+u).
|
||||
4. Threads do not cross from one goal into another goal's threads.
|
||||
5. Mixed siblings: a thread swaps past a sub-goal under the same parent.
|
||||
"""
|
||||
import sys, os, subprocess, time
|
||||
sys.path.insert(0, os.path.dirname(__file__))
|
||||
from visual_lib import *
|
||||
|
||||
TARGET = "test:0.0"
|
||||
AGENT = os.environ.get("AGENT_BIN", "/home/testuser/bin/agent")
|
||||
TRACKER_SERVER = "/home/testuser/bin/tracker-server"
|
||||
ROWS, COLS = 50, 110
|
||||
|
||||
|
||||
def capture_rows():
|
||||
return capture(TARGET, ROWS, COLS)
|
||||
|
||||
|
||||
def dump(label):
|
||||
capture_rows()
|
||||
print(f"\n--- {label} ---")
|
||||
for r in range(ROWS):
|
||||
rt = row_text(r)
|
||||
if rt.strip():
|
||||
print(f" {r:2d}: |{rt}|")
|
||||
|
||||
|
||||
def selected_row():
|
||||
capture_rows()
|
||||
for r in range(ROWS):
|
||||
if row_has_bg(r, "238") > 5:
|
||||
return r
|
||||
return -1
|
||||
|
||||
|
||||
def selected_text():
|
||||
r = selected_row()
|
||||
return row_text(r) if r >= 0 else ""
|
||||
|
||||
|
||||
def navigate_to(text):
|
||||
for _ in range(80):
|
||||
capture_rows()
|
||||
for r in range(ROWS):
|
||||
if row_has_bg(r, "238") > 5:
|
||||
if text in row_text(r):
|
||||
return True
|
||||
break
|
||||
send_keys(TARGET, "e", 0.06)
|
||||
for _ in range(80):
|
||||
capture_rows()
|
||||
for r in range(ROWS):
|
||||
if row_has_bg(r, "238") > 5:
|
||||
if text in row_text(r):
|
||||
return True
|
||||
break
|
||||
send_keys(TARGET, "u", 0.06)
|
||||
return False
|
||||
|
||||
|
||||
def row_of(text):
|
||||
capture_rows()
|
||||
for r in range(ROWS):
|
||||
if text in row_text(r):
|
||||
return r
|
||||
return -1
|
||||
|
||||
|
||||
def open_panel():
|
||||
subprocess.run(
|
||||
["tmux", "send-keys", "-t", TARGET,
|
||||
f"{AGENT} palette --mode goals --window=@0 --path=/tmp --session-name=test --window-name=test0",
|
||||
"Enter"],
|
||||
capture_output=True)
|
||||
time.sleep(1.5)
|
||||
capture_rows()
|
||||
|
||||
|
||||
def reseed(scenario):
|
||||
"""Wipe store and seed the given scenario's tree."""
|
||||
subprocess.run(["pkill", "-f", "palette"], capture_output=True)
|
||||
time.sleep(0.4)
|
||||
cache = os.path.expanduser("~/.cache/agent")
|
||||
subprocess.run(["rm", "-rf", cache], capture_output=True)
|
||||
subprocess.run(["mkdir", "-p", cache], capture_output=True)
|
||||
if scenario == "goals":
|
||||
a = subprocess.run([AGENT, "goal", "add-goal", "--title", "Goal A"], capture_output=True, text=True).stdout.strip()
|
||||
b = subprocess.run([AGENT, "goal", "add-goal", "--title", "Goal B"], capture_output=True, text=True).stdout.strip()
|
||||
subprocess.run([AGENT, "goal", "add-goal", "--title", "Goal C"], capture_output=True)
|
||||
return a, b
|
||||
if scenario == "threads":
|
||||
a = subprocess.run([AGENT, "goal", "add-goal", "--title", "Goal A"], capture_output=True, text=True).stdout.strip()
|
||||
subprocess.run([AGENT, "goal", "add-goal", "--title", "Goal B"], capture_output=True)
|
||||
subprocess.run([AGENT, "goal", "add-thread", "--name", "T1", "--goal", a], capture_output=True)
|
||||
subprocess.run([AGENT, "goal", "add-thread", "--name", "T2", "--goal", a], capture_output=True)
|
||||
subprocess.run([AGENT, "goal", "add-thread", "--name", "T3", "--goal", "goal-b"], capture_output=True)
|
||||
return a
|
||||
if scenario == "mixed":
|
||||
a = subprocess.run([AGENT, "goal", "add-goal", "--title", "Parent"], capture_output=True, text=True).stdout.strip()
|
||||
subprocess.run([AGENT, "goal", "add-goal", "--title", "Sub X", "--parent", a], capture_output=True)
|
||||
subprocess.run([AGENT, "goal", "add-thread", "--name", "Thread T", "--goal", a], capture_output=True)
|
||||
subprocess.run([AGENT, "goal", "add-goal", "--title", "Sub Y", "--parent", a], capture_output=True)
|
||||
return a
|
||||
|
||||
|
||||
def setup():
|
||||
subprocess.run(["tmux", "kill-session", "-t", "test"], capture_output=True)
|
||||
time.sleep(0.2)
|
||||
subprocess.run(["go", "build", "-o", TRACKER_SERVER, "./cmd/tracker-server"],
|
||||
cwd="/home/testuser/agent-tracker", capture_output=True)
|
||||
subprocess.run(["pkill", "-f", "tracker-server"], capture_output=True)
|
||||
time.sleep(0.2)
|
||||
subprocess.Popen([TRACKER_SERVER], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
|
||||
time.sleep(0.5)
|
||||
subprocess.run(["tmux", "new-session", "-d", "-s", "test", "-x", str(COLS), "-y", str(ROWS)],
|
||||
capture_output=True)
|
||||
time.sleep(0.3)
|
||||
|
||||
|
||||
def main():
|
||||
setup()
|
||||
|
||||
# ── 1 & 3: goal swap down + up (no nest) ──
|
||||
print("=== Scenario: root goals ===")
|
||||
reseed("goals")
|
||||
open_panel()
|
||||
dump("initial: Goal A / Goal B / Goal C")
|
||||
if not navigate_to("Goal B"):
|
||||
check("nav to Goal B", False); return 1
|
||||
|
||||
send_keys(TARGET, "alt+e", 0.4)
|
||||
dump("Alt-E on Goal B (expect A C B)")
|
||||
check("Alt-E: Goal B moved below Goal C", row_of("Goal C") < row_of("Goal B"))
|
||||
check("Alt-E: cursor follows Goal B", "Goal B" in selected_text())
|
||||
|
||||
# ── 2: boundary no-op (Goal B is now last) ──
|
||||
before_last = row_of("Goal B")
|
||||
send_keys(TARGET, "alt+e", 0.4)
|
||||
dump("Alt-E on Goal B again (boundary — expect no change)")
|
||||
check("Alt-E boundary: no-op (Goal B still last)", row_of("Goal B") == before_last)
|
||||
|
||||
# ── 3: Alt-U swaps up, does NOT nest ──
|
||||
if not navigate_to("Goal B"):
|
||||
check("nav to Goal B (2)", False); return 1
|
||||
send_keys(TARGET, "alt+u", 0.4)
|
||||
dump("Alt-U on Goal B (expect swap up, NOT nest under Goal A)")
|
||||
check("Alt-U: Goal B swapped above a goal (no nesting)", row_of("Goal B") < row_of("Goal C"))
|
||||
# nesting would indent Goal B; verify Goal B row has root indent (1 leading space = panel padding)
|
||||
gbb = [row_text(r) for r in range(ROWS) if "Goal B" in row_text(r)]
|
||||
if gbb:
|
||||
lead = len(gbb[0]) - len(gbb[0].lstrip())
|
||||
check("Alt-U: Goal B stayed at root depth (no indent)", lead == 1, f"lead={lead}: |{gbb[0]}|")
|
||||
|
||||
# ── 4: threads don't cross goal boundary ──
|
||||
print("\n=== Scenario: threads under two goals ===")
|
||||
reseed("threads")
|
||||
open_panel()
|
||||
dump("initial: Goal A{T1,T2} Goal B{T3}")
|
||||
if not navigate_to("T2"):
|
||||
check("nav to T2", False); return 1
|
||||
t2_before = row_of("T2")
|
||||
send_keys(TARGET, "alt+e", 0.4)
|
||||
dump("Alt-E on T2 (last under Goal A — expect no-op, must NOT join Goal B)")
|
||||
check("Alt-E on last thread: no-op", row_of("T2") == t2_before)
|
||||
check("Alt-E: T2 did not move below T3 / Goal B", row_of("T2") < row_of("T3"))
|
||||
|
||||
# swap T2 up within Goal A
|
||||
send_keys(TARGET, "alt+u", 0.4)
|
||||
dump("Alt-U on T2 (expect T2 above T1 under Goal A)")
|
||||
check("Alt-U: T2 swapped above T1", row_of("T2") < row_of("T1"))
|
||||
|
||||
# ── 5: mixed siblings (thread swaps past sub-goal) ──
|
||||
print("\n=== Scenario: mixed children under Parent ===")
|
||||
reseed("mixed")
|
||||
open_panel()
|
||||
dump("initial: Parent { Sub X, Thread T, Sub Y }")
|
||||
if not navigate_to("Thread T"):
|
||||
check("nav to Thread T", False); return 1
|
||||
send_keys(TARGET, "alt+e", 0.4)
|
||||
dump("Alt-E on Thread T (expect swap past Sub Y)")
|
||||
check("Alt-E mixed: Thread T moved below Sub Y", row_of("Sub Y") < row_of("Thread T"))
|
||||
|
||||
print(summary())
|
||||
return 0 if FAIL == 0 else 1
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
||||
Loading…
Add table
Add a link
Reference in a new issue