@agentikos/omega-os 0.19.39 → 0.19.40

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (37) hide show
  1. package/bootstrap/lib/common.sh +19 -10
  2. package/bootstrap/templates/aisb/architect.md +27 -1
  3. package/bootstrap/templates/aisb/construct.md +27 -1
  4. package/bootstrap/templates/aisb/keymaker.md +27 -1
  5. package/bootstrap/templates/aisb/link.md +27 -1
  6. package/bootstrap/templates/aisb/lmc-protocol.md +27 -1
  7. package/bootstrap/templates/aisb/merovingian.md +27 -1
  8. package/bootstrap/templates/aisb/morpheus.md +27 -1
  9. package/bootstrap/templates/aisb/neo.md +27 -1
  10. package/bootstrap/templates/aisb/niobe.md +27 -1
  11. package/bootstrap/templates/aisb/oracle.md +27 -1
  12. package/bootstrap/templates/aisb/pythia.md +36 -0
  13. package/bootstrap/templates/aisb/seraph.md +27 -1
  14. package/bootstrap/templates/aisb/smith.md +27 -1
  15. package/bootstrap/templates/aisb/zion.md +27 -1
  16. package/omega/Agentik_Engine/omega_engine/__init__.py +1 -1
  17. package/omega/Agentik_Engine/omega_engine/__pycache__/__init__.cpython-313.pyc +0 -0
  18. package/omega/Agentik_Engine/omega_engine/__pycache__/tmux.cpython-313.pyc +0 -0
  19. package/omega/Agentik_Engine/omega_engine/__pycache__/tui.cpython-313.pyc +0 -0
  20. package/omega/Agentik_Engine/omega_engine/tmux.py +45 -26
  21. package/omega/Agentik_Engine/omega_engine/tui.py +25 -20
  22. package/omega/Agentik_Engine/pyproject.toml +1 -1
  23. package/omega/Agentik_Engine/tests/__pycache__/test_install_ux.cpython-313-pytest-8.4.2.pyc +0 -0
  24. package/omega/Agentik_Engine/tests/__pycache__/test_install_ux.cpython-313.pyc +0 -0
  25. package/omega/Agentik_Engine/tests/__pycache__/test_prompt_audit.cpython-313-pytest-8.4.2.pyc +0 -0
  26. package/omega/Agentik_Engine/tests/__pycache__/test_prompt_audit.cpython-313.pyc +0 -0
  27. package/omega/Agentik_Engine/tests/__pycache__/test_tmux_palette.cpython-313-pytest-8.4.2.pyc +0 -0
  28. package/omega/Agentik_Engine/tests/__pycache__/test_tmux_palette.cpython-313.pyc +0 -0
  29. package/omega/Agentik_Engine/tests/__pycache__/test_tui_runtime.cpython-313-pytest-8.4.2.pyc +0 -0
  30. package/omega/Agentik_Engine/tests/__pycache__/test_tui_runtime.cpython-313.pyc +0 -0
  31. package/omega/Agentik_Engine/tests/test_install_ux.py +87 -2
  32. package/omega/Agentik_Engine/tests/test_prompt_audit.py +82 -0
  33. package/omega/Agentik_Engine/tests/test_tmux_palette.py +94 -0
  34. package/omega/Agentik_Engine/tests/test_tui_runtime.py +50 -0
  35. package/omega/Agentik_SSOT/VERSION +1 -1
  36. package/omega/Agentik_SSOT/docs/AUDIT-V0.19.40.md +163 -0
  37. package/package.json +1 -1
@@ -583,7 +583,7 @@ set -s escape-time 10
583
583
  # session (AISB / oracle / worker / dev / linear / home / other) without
584
584
  # leaving tmux.
585
585
  # ════════════════════════════════════════════════════════════════════
586
- bind-key Z display-popup -E -w 80% -h 80% "omega tmux menu"
586
+ bind-key Z display-popup -E -w 100% -h 100% "omega tmux menu"
587
587
 
588
588
  # Prefix+S — native session list (tmux's built-in choose-tree)
589
589
  bind-key S choose-tree -Zs
@@ -591,18 +591,28 @@ bind-key S choose-tree -Zs
591
591
  # Option+/ → session switcher (fzf popup, pick any live tmux session)
592
592
  # Option+z → Omega action menu (spawn Omega if missing + switch-client)
593
593
  # macOS Option key → enable "Use Option as Meta" in your terminal.
594
- bind-key -n M-/ display-popup -E -h 80% -w 90% "omega tmux switcher"
594
+ bind-key -n M-/ display-popup -E -w 100% -h 100% "omega tmux switcher"
595
595
  bind-key -n M-z run-shell -b "tmux has-session -t Omega 2>/dev/null || tmux new-session -d -s Omega -n menu -c $HOME 'omega menu-tui'; tmux switch-client -t Omega"
596
596
 
597
597
  # ════════════════════════════════════════════════════════════════════
598
- # Claude Code LIGHT theme (white-paper)
598
+ # Pixel/CRT amber theme (tmux-claude inspired)
599
+ # xterm colour137 ≈ #af8700 — amber/gold on whatever your terminal bg is.
600
+ # Theme-neutral: inherits terminal background, works in light + dark.
599
601
  # ════════════════════════════════════════════════════════════════════
600
- set -g status-style "bg=#FAFAF7,fg=#3D3929"
601
- set -g status-left "#[fg=#D97757,bold]Ω #S #[fg=#A8A29E] "
602
- set -g status-right "#[fg=#88837A]%H:%M #(omega tmux count 2>/dev/null) sessions"
603
- set -g pane-border-style "fg=#A8A29E"
604
- set -g pane-active-border-style "fg=#D97757"
605
- set -g message-style "bg=#E5E2DD,fg=#3D3929"
602
+ set -g status-style "fg=default"
603
+ set -g status-left ' #[fg=colour137,bold]Ω #S #[fg=default,nobold] #[fg=colour137]#(omega tmux count 2>/dev/null) sess #[fg=default]• #[fg=colour137]%H:%M '
604
+ set -g status-right '#[fg=default]TS #[fg=colour137]#(omega tmux count 2>/dev/null) #[fg=default]• #[fg=colour137]Ω '
605
+
606
+ # Hide the window list (tmux-claude clean look).
607
+ setw -g window-status-format ''
608
+ setw -g window-status-current-format ''
609
+ setw -g window-status-separator ''
610
+
611
+ # Pane borders — amber on active, inherit on inactive.
612
+ set -g pane-border-style "fg=default"
613
+ set -g pane-active-border-style "fg=colour137"
614
+ set -g message-style "fg=colour137,bg=default,bold"
615
+ set -g mode-style "fg=default,bg=colour137"
606
616
  """
607
617
 
608
618
 
@@ -681,7 +691,7 @@ set -g @scroll-without-changing-pane on
681
691
  # Prefix+Z — OmegaOS session picker
682
692
  # Opens a tmux popup with `omega tmux menu`.
683
693
  # ════════════════════════════════════════════════════════════════════
684
- bind-key Z display-popup -E -w 80% -h 80% "omega tmux menu"
694
+ bind-key Z display-popup -E -w 100% -h 100% "omega tmux menu"
685
695
 
686
696
  # Prefix+S — native choose-tree
687
697
  bind-key S choose-tree -Zs
@@ -709,30 +719,39 @@ bind-key r source-file ~/.tmux.conf \\; display-message "tmux.conf reloaded"
709
719
  # ────────────────────────────────────────────────────────────────────
710
720
  # popup -E runs the command in a tmux popup overlay (modal). The popup
711
721
  # closes when the command exits, returning the user to their previous
712
- # pane. -h 80% / -w 90% sizes the popup.
713
- bind-key -n M-/ display-popup -E -h 80% -w 90% "omega tmux switcher"
722
+ # pane. -w 100% -h 100% gives a full-screen popup (tmux-claude style).
723
+ bind-key -n M-/ display-popup -E -w 100% -h 100% "omega tmux switcher"
714
724
  bind-key -n M-z run-shell -b "tmux has-session -t Omega 2>/dev/null || tmux new-session -d -s Omega -n menu -c $HOME 'omega menu-tui'; tmux switch-client -t Omega"
715
725
 
716
726
  # ════════════════════════════════════════════════════════════════════
717
- # Claude Code LIGHT theme (white-paper) — status bar + borders
727
+ # Pixel/CRT amber theme (tmux-claude inspired)
718
728
  # ════════════════════════════════════════════════════════════════════
719
- # Colours derived from tweakcn.com/r/themes/claude.json:
720
- # bg #FAFAF7 (cream) fg #3D3929 (slate) accent #D97757 (orange)
721
- # muted #88837A (warm gray) border #A8A29E (soft)
722
- set -g status-style "bg=#FAFAF7,fg=#3D3929"
723
- set -g status-left "#[fg=#D97757,bold]Ω #S #[fg=#A8A29E]│ "
724
- set -g status-right "#[fg=#88837A]%H:%M #(omega tmux count 2>/dev/null) sessions"
729
+ # xterm colour137 #af8700 — amber/gold. Theme-neutral: status bar
730
+ # inherits the terminal background, so it looks right in any color
731
+ # scheme (dark, light, solarized, etc.).
732
+ #
733
+ # Bullets `•` everywhere as separators, accent colour137 for values,
734
+ # default fg for labels. Window list HIDDEN (tmux-claude clean look)
735
+ # the operator switches via Option+/ or `omega tmux menu` instead.
736
+ set -g status-style "fg=default"
737
+ set -g status-left ' #[fg=colour137,bold]Ω #S #[fg=default,nobold]• #[fg=colour137]#(omega tmux count 2>/dev/null) sess #[fg=default]• #[fg=colour137]%H:%M '
738
+ set -g status-right '#[fg=default]CPU #[fg=colour137]#(awk "{print int(\\$1*100/$(nproc))}" /proc/loadavg 2>/dev/null || echo --)% #[fg=default]• #[fg=colour137]Ω v#(cat $OMEGA_HOME/Agentik_SSOT/VERSION 2>/dev/null || echo dev) '
725
739
  set -g status-interval 5
726
- set -g status-left-length 40
740
+ set -g status-left-length 60
727
741
  set -g status-right-length 60
728
742
 
729
- # Pane borders soft warm gray, accent orange on the active pane.
730
- set -g pane-border-style "fg=#A8A29E"
731
- set -g pane-active-border-style "fg=#D97757"
743
+ # Hide the window list (tmux-claude clean look).
744
+ setw -g window-status-format ''
745
+ setw -g window-status-current-format ''
746
+ setw -g window-status-separator ''
747
+
748
+ # Pane borders — amber on active, inherit on inactive.
749
+ set -g pane-border-style "fg=default"
750
+ set -g pane-active-border-style "fg=colour137"
732
751
 
733
- # Message + copy-mode prompts — same palette.
734
- set -g message-style "bg=#E5E2DD,fg=#3D3929"
735
- set -g mode-style "bg=#D97757,fg=#FAFAF7"
752
+ # Message + copy-mode prompts — amber on terminal bg.
753
+ set -g message-style "fg=colour137,bg=default,bold"
754
+ set -g mode-style "fg=default,bg=colour137"
736
755
  """
737
756
 
738
757
 
@@ -441,11 +441,16 @@ def _arrow_menu() -> int:
441
441
  OMEGA_BIN = str(HOME / "Agentik_Tools" / "bin" / "omega")
442
442
 
443
443
  # ANSI escapes — used via fzf --ansi.
444
- ORANGE = "\033[38;2;217;119;87m"
445
- MUTED = "\033[38;2;136;131;122m"
446
- BOLD = "\033[1m"
447
- DIM = "\033[2m"
448
- RST = "\033[0m"
444
+ # Pixel/CRT amber palette — colour137 family.
445
+ # Inherits terminal background (no cream); accent = amber gold.
446
+ AMBER = "\033[38;5;137m" # xterm 256 colour137 = amber gold
447
+ AMBER_BR = "\033[38;5;215m" # brighter amber for active/highlighted rows
448
+ MUTED = "\033[38;5;240m" # dim gray
449
+ BOLD = "\033[1m"
450
+ DIM = "\033[2m"
451
+ RST = "\033[0m"
452
+ # Backward-compat alias — existing code may reference ORANGE.
453
+ ORANGE = AMBER
449
454
 
450
455
  def _label(name: str, hint: str = "") -> str:
451
456
  """Two-column label: name on the left, hint dimmed on the right."""
@@ -469,7 +474,7 @@ def _arrow_menu() -> int:
469
474
  # a short "MENU" section. Reasoning: the user spends 99% of their
470
475
  # time in conversations — the menu should reflect that, not bury the
471
476
  # chats below 6 sections of admin options.
472
- DOT_ON = f"{ORANGE}●{RST}" # alive
477
+ DOT_ON = f"{AMBER}●{RST}" # alive
473
478
  DOT_OFF = f"{MUTED}○{RST}" # not running
474
479
 
475
480
  def _dot(alive: bool) -> str:
@@ -700,20 +705,20 @@ def _arrow_menu() -> int:
700
705
  "--pointer=▶",
701
706
  "--marker=●",
702
707
  "--color="
703
- "bg:#FAFAF7,"
704
- "fg:#3D3929,"
705
- "bg+:#E5E2DD,"
706
- "fg+:#D97757,"
707
- "hl:#D97757,"
708
- "hl+:#D97757,"
709
- "prompt:#D97757,"
710
- "pointer:#D97757,"
711
- "marker:#D97757,"
712
- "header:#88837A,"
713
- "border:#A8A29E,"
714
- "info:#88837A,"
715
- "spinner:#D97757,"
716
- "gutter:#FAFAF7"],
708
+ "bg:-1,"
709
+ "fg:default,"
710
+ "bg+:#1c1c1c,"
711
+ "fg+:colour215,"
712
+ "hl:colour137,"
713
+ "hl+:colour215,"
714
+ "prompt:colour137,"
715
+ "pointer:colour137,"
716
+ "marker:colour215,"
717
+ "header:colour137,"
718
+ "border:colour137,"
719
+ "info:colour137,"
720
+ "spinner:colour137,"
721
+ "gutter:-1"],
717
722
  input="\n".join(lines), capture_output=True, text=True,
718
723
  )
719
724
  except (KeyboardInterrupt, subprocess.SubprocessError):
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "omega-engine"
3
- version = "0.19.39"
3
+ version = "0.19.40"
4
4
  description = "The Omega OS orchestration engine — event-sourced, verified-completion agent graphs."
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.11"
@@ -18,8 +18,14 @@ COMMON_SH = REPO_ROOT / "bootstrap" / "lib" / "common.sh"
18
18
 
19
19
 
20
20
  def _bash(snippet: str, *, env: dict[str, str] | None = None,
21
- stdin: str = "") -> tuple[int, str, str]:
22
- """Run a bash snippet that sources common.sh; return (rc, stdout, stderr)."""
21
+ stdin: str = "", raw: bool = False) -> tuple[int, str, str]:
22
+ """Run a bash snippet that sources common.sh; return (rc, stdout, stderr).
23
+
24
+ raw=True disables Python's universal-newlines translation so callers can
25
+ distinguish CR (\\r — progress-bar overwrite) from LF (\\n — newline).
26
+ Without it, `text=True` collapses both into "\\n" and tests that count
27
+ newlines lose the signal they need.
28
+ """
23
29
  base_env = {**os.environ}
24
30
  base_env.setdefault("OMEGA_REPO", str(REPO_ROOT))
25
31
  base_env.setdefault("OMEGA_HOME", "/tmp/omega-ux-pytest")
@@ -40,6 +46,14 @@ def _bash(snippet: str, *, env: dict[str, str] | None = None,
40
46
  "C_BOLD=''; C_CYAN=''; C_GREEN=''; C_YELLOW=''; C_RED=''; C_MAGENTA=''; C_BLUE=''\n"
41
47
  + snippet
42
48
  )
49
+ if raw:
50
+ proc = subprocess.run(
51
+ ["bash", "-c", code],
52
+ env=base_env, capture_output=True, input=stdin.encode("utf-8"),
53
+ timeout=15,
54
+ )
55
+ return proc.returncode, proc.stdout.decode("utf-8", "replace"), \
56
+ proc.stderr.decode("utf-8", "replace")
43
57
  proc = subprocess.run(
44
58
  ["bash", "-c", code],
45
59
  env=base_env, capture_output=True, text=True, input=stdin,
@@ -131,6 +145,77 @@ class TestPathSetup(unittest.TestCase):
131
145
  self.assertEqual(rc_path.read_text(), "# only this\n")
132
146
 
133
147
 
148
+ class TestProgressBarCountingDiscipline(unittest.TestCase):
149
+ """Regression tests for the v0.19.x progress-bar double-count bug.
150
+
151
+ `run_step` calls `_full_progress` TWICE per step ("run" then "ok"). The
152
+ old implementation incremented STEP_COUNT on EVERY call, which with
153
+ STEP_TOTAL=21 drove the counter to 42, the bar to 200%, and the "current
154
+ >= total → newline" branch fired four+ times — so the user saw the
155
+ progress bar spawn fresh lines mid-install instead of overwriting one
156
+ single line. The fix: only "ok"/"skip" count as real progress; "run"
157
+ just re-paints the bar with the in-progress step name.
158
+ """
159
+
160
+ def test_full_progress_counts_only_ok_and_skip(self):
161
+ rc, out, _ = _bash(
162
+ "export STEP_TOTAL=10\n"
163
+ "_full_progress a run\n"
164
+ "_full_progress a ok\n"
165
+ "_full_progress b run\n"
166
+ "_full_progress b skip\n"
167
+ "echo STEP_COUNT=$STEP_COUNT\n"
168
+ )
169
+ self.assertEqual(rc, 0)
170
+ # Two runs + one ok + one skip → count should be 2 (only ok+skip count).
171
+ self.assertIn("STEP_COUNT=2", out)
172
+
173
+ def test_no_premature_newline_in_middle_of_run(self):
174
+ # STEP_TOTAL=2 so the second "ok" hits the boundary; nothing before
175
+ # that may emit a newline. With the old code, the boundary fired on
176
+ # the very FIRST "ok" (count had ticked to 2 already because run+ok
177
+ # = +2 instead of +1), producing a stray mid-install newline.
178
+ # raw=True so Python doesn't fold \r into \n via universal-newlines.
179
+ rc, out, _ = _bash(
180
+ "export STEP_TOTAL=2\n"
181
+ "_full_progress a run\n"
182
+ "_full_progress a ok\n"
183
+ "_full_progress b run\n"
184
+ "_full_progress b ok\n",
185
+ raw=True,
186
+ )
187
+ self.assertEqual(rc, 0)
188
+ # Frames are separated by \r (carriage return → overwrite the line),
189
+ # NOT \n. Exactly one \n — emitted ONLY after the final "ok" crosses total.
190
+ self.assertEqual(out.count("\n"), 1,
191
+ f"expected 1 trailing newline, got {out.count(chr(10))}: {out!r}")
192
+ # Carriage returns prove the frames overwrote rather than scrolled.
193
+ self.assertGreaterEqual(out.count("\r"), 3)
194
+ # Final frame must read 2/2 100% — not 3/2 150% or 4/2 200% like the bug.
195
+ self.assertIn("2/2", out)
196
+ self.assertIn("100%", out)
197
+ self.assertNotIn("3/2", out)
198
+ self.assertNotIn("4/2", out)
199
+ self.assertNotIn("150%", out)
200
+ self.assertNotIn("200%", out)
201
+
202
+ def test_run_phase_shows_in_progress_step_name(self):
203
+ # The "run" phase should re-paint with the step name being attempted
204
+ # so the user sees what's currently happening, even though the count
205
+ # hasn't bumped yet.
206
+ rc, out, _ = _bash(
207
+ "export STEP_TOTAL=5\n"
208
+ "_full_progress mystep run\n",
209
+ raw=True,
210
+ )
211
+ self.assertEqual(rc, 0)
212
+ self.assertIn("mystep", out)
213
+ # Count is still 0 because nothing has finished yet.
214
+ self.assertIn("0/5", out)
215
+ # And critically — no newline during a "run" frame.
216
+ self.assertEqual(out.count("\n"), 0)
217
+
218
+
134
219
  class TestPostInstallCard(unittest.TestCase):
135
220
  def test_card_includes_status_and_next_steps(self):
136
221
  # omega CLI isn't on PATH inside the test sandbox → readiness=UNKNOWN
@@ -195,5 +195,87 @@ class TestAuditAisbSuiteAgainstRealRepo(unittest.TestCase):
195
195
  self.assertLessEqual(overlap, 1.0)
196
196
 
197
197
 
198
+ class TestEnrichedRolePromptsRegression(unittest.TestCase):
199
+ """v0.19.40 — every AISB role prompt was enriched with a uniform
200
+ `THREE LAWS + Operating Contract` block (Three Laws + LMC protocol
201
+ ref + .done.json contract + done-marker + fresh-context handoff).
202
+ The audit went from 52.0/100 average → 98.3/100. These tests lock in
203
+ that quality floor so a future template edit can't silently drop a
204
+ role below the contract."""
205
+
206
+ def test_every_aisb_role_scores_at_least_80(self):
207
+ if not TEMPLATES.is_dir():
208
+ self.skipTest("AISB templates not present in repo")
209
+ with tempfile.TemporaryDirectory() as tmp:
210
+ home = _seed_real_aisb(Path(tmp))
211
+ report = audit_aisb_suite(home)
212
+ below = [(r.agent_id, r.score) for r in report.per_agent if r.score < 80]
213
+ self.assertEqual(below, [],
214
+ f"v0.19.40 contract requires every role ≥80/100. "
215
+ f"Regression: {below} — re-enrich the failing role with "
216
+ f"the THREE LAWS + Operating Contract block.")
217
+
218
+ def test_average_suite_score_at_least_85(self):
219
+ if not TEMPLATES.is_dir():
220
+ self.skipTest("AISB templates not present in repo")
221
+ with tempfile.TemporaryDirectory() as tmp:
222
+ home = _seed_real_aisb(Path(tmp))
223
+ report = audit_aisb_suite(home)
224
+ self.assertGreaterEqual(
225
+ report.average_score, 85.0,
226
+ f"v0.19.40 baseline: 98.3 average. Floor: 85. "
227
+ f"Got {report.average_score:.1f} — investigate which roles "
228
+ f"lost contract references.")
229
+
230
+ def test_done_json_vocab_overlap_at_least_80_percent(self):
231
+ """Before v0.19.40 only 33% of roles referenced `.done.json`
232
+ in their on-disk file (the rest relied on the loader to concat
233
+ lmc-protocol.md at spawn). The enrichment brought it to 100%."""
234
+ if not TEMPLATES.is_dir():
235
+ self.skipTest("AISB templates not present in repo")
236
+ with tempfile.TemporaryDirectory() as tmp:
237
+ home = _seed_real_aisb(Path(tmp))
238
+ oh = orchestration_health(home)
239
+ self.assertGreaterEqual(
240
+ oh["shared_vocab_overlap"], 0.80,
241
+ f"`.done.json` vocab overlap must stay ≥80% — "
242
+ f"got {oh['shared_vocab_overlap']*100:.0f}%. "
243
+ f"A role file lost its done.json reference.")
244
+
245
+ def test_every_role_references_lmc_protocol(self):
246
+ """The LMC protocol must be named in every role file (not just
247
+ implicit via the loader's concat). Direct grep check."""
248
+ if not TEMPLATES.is_dir():
249
+ self.skipTest("AISB templates not present in repo")
250
+ import re as _re
251
+ lmc_re = _re.compile(
252
+ r"(lmc[\s\-_]*(protocol|gate)?|lead[\s\-]+manager[\s\-]+checker|"
253
+ r"lmc-protocol\.md)",
254
+ _re.IGNORECASE,
255
+ )
256
+ missing = []
257
+ for md in sorted(TEMPLATES.glob("*.md")):
258
+ if not lmc_re.search(md.read_text()):
259
+ missing.append(md.name)
260
+ self.assertEqual(missing, [],
261
+ f"Every AISB role must reference the LMC protocol — "
262
+ f"missing in: {missing}")
263
+
264
+ def test_every_role_references_three_laws(self):
265
+ """Three Laws must be present (not just two). Pre-v0.19.40
266
+ most roles only had LAW 1 + LAW 2. Now LAW 3 is mandatory."""
267
+ if not TEMPLATES.is_dir():
268
+ self.skipTest("AISB templates not present in repo")
269
+ missing_third = []
270
+ for md in sorted(TEMPLATES.glob("*.md")):
271
+ text = md.read_text()
272
+ # Match LAW 3 or 'Third Law' or 'law 3' — same regex as the audit.
273
+ if "LAW 3" not in text and "Third Law" not in text.lower():
274
+ missing_third.append(md.name)
275
+ self.assertEqual(missing_third, [],
276
+ f"Every AISB role must reference LAW 3 (autonomous execution). "
277
+ f"Missing in: {missing_third}")
278
+
279
+
198
280
  if __name__ == "__main__":
199
281
  unittest.main()
@@ -0,0 +1,94 @@
1
+ """Regression tests for the bundled tmux palette.
2
+
3
+ We swapped the Claude-cream/orange paper theme (#FAFAF7 / #D97757) for
4
+ the tmux-claude pixel/CRT amber palette (xterm colour137 ≈ #af8700).
5
+ These tests pin the new palette so a future drift back to the old
6
+ theme fails loudly.
7
+
8
+ Pinned behaviour:
9
+
10
+ 1. The pro config uses ``colour137`` (the amber accent) in multiple
11
+ places — status bar, pane borders, message style.
12
+ 2. The old Claude paper palette (#FAFAF7 cream + #D97757 orange) is
13
+ completely gone from the pro config.
14
+ 3. The window list is hidden (tmux-claude clean look — the operator
15
+ navigates via Option+/ or `omega tmux menu`, not the window bar).
16
+ 4. The popup keybinds use the full-screen ``-w 100% -h 100%`` sizing
17
+ from tmux-claude (the old config used the smaller 80%/90% boxes).
18
+ """
19
+ from __future__ import annotations
20
+
21
+ import sys
22
+ import unittest
23
+ from pathlib import Path
24
+
25
+ HERE = Path(__file__).resolve().parent
26
+ sys.path.insert(0, str(HERE.parent))
27
+
28
+ from omega_engine.tmux import _PRO_CONFIG # noqa: E402
29
+
30
+
31
+ class TestTmuxClaudePalette(unittest.TestCase):
32
+ def test_pro_config_uses_colour137(self):
33
+ """Amber accent ``colour137`` must show up in at least 4 places.
34
+
35
+ Typically: status-left accent, status-right accent, pane-active
36
+ border, message-style — plus possibly mode-style. We assert >= 4
37
+ so a partial revert still trips the test.
38
+ """
39
+ n = _PRO_CONFIG.count("colour137")
40
+ self.assertGreaterEqual(
41
+ n, 4,
42
+ f"_PRO_CONFIG must reference colour137 at least 4 times "
43
+ f"(status accents + active border + message/mode style); "
44
+ f"found {n}",
45
+ )
46
+
47
+ def test_no_claude_cream_orange_palette(self):
48
+ """The old paper theme (#FAFAF7 + #D97757) must be gone."""
49
+ self.assertNotIn(
50
+ "#FAFAF7", _PRO_CONFIG,
51
+ "Old Claude cream bg #FAFAF7 must not appear in _PRO_CONFIG",
52
+ )
53
+ self.assertNotIn(
54
+ "#D97757", _PRO_CONFIG,
55
+ "Old Claude orange accent #D97757 must not appear in _PRO_CONFIG",
56
+ )
57
+
58
+ def test_window_status_hidden(self):
59
+ """Window list is hidden — both empty format strings present."""
60
+ self.assertIn(
61
+ "window-status-format ''", _PRO_CONFIG,
62
+ "Inactive window list must be hidden (window-status-format '')",
63
+ )
64
+ self.assertIn(
65
+ "window-status-current-format ''", _PRO_CONFIG,
66
+ "Active window must be hidden too (window-status-current-format '')",
67
+ )
68
+
69
+ def test_popup_keybinds_use_full_screen(self):
70
+ """The M-/ session switcher popup uses full-screen sizing."""
71
+ self.assertIn(
72
+ "-w 100% -h 100%", _PRO_CONFIG,
73
+ "Popup keybinds must use full-screen tmux-claude sizing "
74
+ "(-w 100% -h 100%)",
75
+ )
76
+ # Specifically the M-/ binding (the canonical session switcher).
77
+ # We grep the relevant line to make sure 100%/100% is wired there
78
+ # — not just present elsewhere by coincidence.
79
+ m_slash_lines = [
80
+ ln for ln in _PRO_CONFIG.splitlines() if "M-/" in ln
81
+ ]
82
+ self.assertTrue(
83
+ m_slash_lines,
84
+ "_PRO_CONFIG must keep the M-/ popup binding",
85
+ )
86
+ self.assertTrue(
87
+ any("-w 100% -h 100%" in ln for ln in m_slash_lines),
88
+ "M-/ popup binding must use -w 100% -h 100% "
89
+ "(full-screen tmux-claude style)",
90
+ )
91
+
92
+
93
+ if __name__ == "__main__":
94
+ unittest.main(verbosity=2)
@@ -269,6 +269,56 @@ class TestChatFirstRedesign(unittest.TestCase):
269
269
  self.assertIn("paperclip_bridge", src)
270
270
 
271
271
 
272
+ class TestPixelPalette(unittest.TestCase):
273
+ """Pixel/CRT amber palette — colour137 family. Locks in the
274
+ replacement of the Claude cream/orange palette with a retro-CRT
275
+ amber accent that inherits the terminal background, matching the
276
+ look-and-feel reference from agentik-os/tmux-claude."""
277
+
278
+ def test_amber_constant_defined(self):
279
+ """`AMBER` must be declared in `_arrow_menu` — it's the new
280
+ primary accent color (xterm 256 colour137 = amber gold)."""
281
+ import inspect
282
+ from omega_engine.tui import _arrow_menu
283
+ src = inspect.getsource(_arrow_menu)
284
+ self.assertIn("AMBER", src,
285
+ "_arrow_menu must declare AMBER (the new colour137 accent)")
286
+
287
+ def test_colour137_in_fzf_args(self):
288
+ """The fzf `--color=` block must use the xterm `colour137`
289
+ named amber — that's the look-and-feel reference."""
290
+ import inspect
291
+ from omega_engine.tui import _arrow_menu
292
+ src = inspect.getsource(_arrow_menu)
293
+ self.assertIn("colour137", src,
294
+ "fzf --color= block must use colour137 (amber gold) — "
295
+ "the pixel/CRT palette references it explicitly")
296
+
297
+ def test_no_claude_cream_orange_in_fzf(self):
298
+ """The old Claude cream `#FAFAF7` background must NOT appear in
299
+ `_arrow_menu` anymore — the new palette inherits the terminal
300
+ background (`bg:-1`) for a transparent retro feel."""
301
+ import inspect
302
+ from omega_engine.tui import _arrow_menu
303
+ src = inspect.getsource(_arrow_menu)
304
+ self.assertNotIn("#FAFAF7", src,
305
+ "_arrow_menu must NOT contain Claude cream #FAFAF7 — "
306
+ "the new palette inherits the terminal background")
307
+
308
+ def test_orange_aliased_to_amber(self):
309
+ """Backward-compat: `ORANGE` must alias `AMBER` so existing
310
+ references (`_section`, `_label`, prompts) keep working with
311
+ only a color change, no logic change."""
312
+ import inspect
313
+ from omega_engine.tui import _arrow_menu
314
+ src = inspect.getsource(_arrow_menu)
315
+ # Tolerate any reasonable spacing around `=`.
316
+ import re
317
+ self.assertRegex(src, r"ORANGE\s*=\s*AMBER",
318
+ "ORANGE must alias AMBER (backward-compat for existing "
319
+ "_section/_label/prompt code)")
320
+
321
+
272
322
  class TestOmegaWindowAliveHelper(unittest.TestCase):
273
323
  """tmux.omega_window_alive() — the helper the chat-first TUI uses
274
324
  to know whether AISB-chat / Hermès-chat are running."""
@@ -1 +1 @@
1
- 0.19.39
1
+ 0.19.40