1
0

fix: Docker-first test suite, UX improvements, and bug fixes (#100)

Dashboard UX:
- Restructure nav from 22 flat links to 6 core + MORE dropdown
- Add mobile nav section labels (Core, Intelligence, Agents, System, Commerce)
- Defer marked.js and dompurify.js loading, consolidate CDN to jsdelivr
- Optimize font weights (drop unused 300/500), bump style.css cache buster
- Remove duplicate HTMX load triggers from sidebar and health panels

Bug fixes:
- Fix Timmy showing OFFLINE by registering after swarm recovery sweep
- Fix ThinkingEngine await bug with asyncio.run_coroutine_threadsafe
- Fix chat auto-scroll by calling scrollChat() after history partial loads
- Add missing /voice/button page and /voice/command endpoint
- Fix Grok api_key="" treated as falsy falling through to env key
- Fix self_modify PROJECT_ROOT using settings.repo_root instead of __file__

Docker test infrastructure:
- Bind-mount hands/, docker/, Dockerfiles, and compose files into test container
- Add fontconfig + fonts-dejavu-core for creative/assembler TextClip tests
- Initialize minimal git repo in Dockerfile.test for GitSafety compatibility
- Fix introspection and path resolution tests for Docker /app context

All 1863 tests pass in Docker (0 failures, 77 skipped).

Co-authored-by: Alexander Payne <apayne@MM.local>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Alexander Whitestone
2026-02-28 22:14:37 -05:00
committed by GitHub
parent 6e67c3b421
commit 89cfe1be0d
12 changed files with 194 additions and 55 deletions

View File

@@ -9,13 +9,14 @@
<title>{% block title %}Timmy Time — Mission Control{% endblock %}</title>
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link href="https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@300;400;500;700&display=swap" rel="stylesheet" />
<link rel="preconnect" href="https://cdn.jsdelivr.net" crossorigin />
<link href="https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400;700&display=swap" rel="stylesheet" />
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-QWTKZyjpPEjISv5WaRU9OFeRpok6YctnYmDr5pNlyT2bRjXh0JMhjY6hW+ALEwIH" crossorigin="anonymous" />
<link rel="stylesheet" href="/static/style.css?v=4" />
<link rel="stylesheet" href="/static/style.css?v=5" />
{% block extra_styles %}{% endblock %}
<script src="https://unpkg.com/htmx.org@2.0.3/dist/htmx.min.js" crossorigin="anonymous"></script>
<script src="https://cdn.jsdelivr.net/npm/marked@15.0.7/marked.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/dompurify@3.2.4/dist/purify.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/htmx.org@2.0.3/dist/htmx.min.js" crossorigin="anonymous"></script>
<script defer src="https://cdn.jsdelivr.net/npm/marked@15.0.7/marked.min.js"></script>
<script defer src="https://cdn.jsdelivr.net/npm/dompurify@3.2.4/dist/purify.min.js"></script>
</head>
<body>
<header class="mc-header">
@@ -28,26 +29,31 @@
<div class="mc-header-right mc-desktop-nav">
<a href="/tasks" class="mc-test-link">TASKS</a>
<a href="/briefing" class="mc-test-link">BRIEFING</a>
<a href="/thinking" class="mc-test-link" style="color:#c084fc;">THINKING</a>
<a href="/swarm/mission-control" class="mc-test-link">MISSION CONTROL</a>
<a href="/thinking" class="mc-test-link mc-link-thinking">THINKING</a>
<a href="/swarm/mission-control" class="mc-test-link">MISSION CTRL</a>
<a href="/swarm/live" class="mc-test-link">SWARM</a>
<a href="/spark/ui" class="mc-test-link">SPARK</a>
<a href="/marketplace/ui" class="mc-test-link">MARKET</a>
<a href="/tools" class="mc-test-link">TOOLS</a>
<a href="/swarm/events" class="mc-test-link">EVENTS</a>
<a href="/bugs" class="mc-test-link" style="color:#ff6b6b;">BUGS</a>
<a href="/lightning/ledger" class="mc-test-link">LEDGER</a>
<a href="/memory" class="mc-test-link">MEMORY</a>
<a href="/router/status" class="mc-test-link">ROUTER</a>
<a href="/grok/status" class="mc-test-link" style="color:#00ff88;">GROK</a>
<a href="/self-modify/queue" class="mc-test-link">UPGRADES</a>
<a href="/self-coding" class="mc-test-link">SELF-CODING</a>
<a href="/hands" class="mc-test-link">HANDS</a>
<a href="/work-orders/queue" class="mc-test-link">WORK ORDERS</a>
<a href="/creative/ui" class="mc-test-link">CREATIVE</a>
<a href="/voice/button" class="mc-test-link">VOICE</a>
<a href="/mobile" class="mc-test-link" title="Mobile-optimized view">MOBILE</a>
<a href="/mobile/local" class="mc-test-link" title="Local AI on iPhone">LOCAL AI</a>
<a href="/bugs" class="mc-test-link mc-link-bugs">BUGS</a>
<div class="mc-nav-dropdown">
<button class="mc-test-link mc-dropdown-toggle" aria-expanded="false">MORE &#x25BE;</button>
<div class="mc-dropdown-menu">
<a href="/spark/ui" class="mc-test-link">SPARK</a>
<a href="/marketplace/ui" class="mc-test-link">MARKET</a>
<a href="/tools" class="mc-test-link">TOOLS</a>
<a href="/swarm/events" class="mc-test-link">EVENTS</a>
<a href="/lightning/ledger" class="mc-test-link">LEDGER</a>
<a href="/memory" class="mc-test-link">MEMORY</a>
<a href="/router/status" class="mc-test-link">ROUTER</a>
<a href="/grok/status" class="mc-test-link mc-link-grok">GROK</a>
<a href="/self-modify/queue" class="mc-test-link">UPGRADES</a>
<a href="/self-coding" class="mc-test-link">SELF-CODING</a>
<a href="/hands" class="mc-test-link">HANDS</a>
<a href="/work-orders/queue" class="mc-test-link">WORK ORDERS</a>
<a href="/creative/ui" class="mc-test-link">CREATIVE</a>
<a href="/voice/button" class="mc-test-link">VOICE</a>
<a href="/mobile" class="mc-test-link" title="Mobile-optimized view">MOBILE</a>
<a href="/mobile/local" class="mc-test-link" title="Local AI on iPhone">LOCAL AI</a>
</div>
</div>
<button id="enable-notifications" class="mc-test-link" style="background:none;cursor:pointer;" title="Enable notifications">&#x1F514;</button>
<span class="mc-time" id="clock"></span>
</div>
@@ -66,24 +72,29 @@
<span class="mc-time" id="clock-mobile"></span>
</div>
<a href="/" class="mc-mobile-link">HOME</a>
<div class="mc-mobile-section-label">CORE</div>
<a href="/tasks" class="mc-mobile-link">TASKS</a>
<a href="/briefing" class="mc-mobile-link">BRIEFING</a>
<a href="/thinking" class="mc-mobile-link">THINKING</a>
<a href="/swarm/mission-control" class="mc-mobile-link">MISSION CONTROL</a>
<a href="/swarm/live" class="mc-mobile-link">SWARM</a>
<a href="/spark/ui" class="mc-mobile-link">SPARK</a>
<a href="/marketplace/ui" class="mc-mobile-link">MARKET</a>
<a href="/tools" class="mc-mobile-link">TOOLS</a>
<a href="/swarm/events" class="mc-mobile-link">EVENTS</a>
<a href="/bugs" class="mc-mobile-link">BUGS</a>
<a href="/lightning/ledger" class="mc-mobile-link">LEDGER</a>
<div class="mc-mobile-section-label">INTELLIGENCE</div>
<a href="/spark/ui" class="mc-mobile-link">SPARK</a>
<a href="/memory" class="mc-mobile-link">MEMORY</a>
<a href="/router/status" class="mc-mobile-link">ROUTER</a>
<a href="/grok/status" class="mc-mobile-link">GROK</a>
<a href="/self-modify/queue" class="mc-mobile-link">UPGRADES</a>
<a href="/self-coding" class="mc-mobile-link">SELF-CODING</a>
<a href="/marketplace/ui" class="mc-mobile-link">MARKET</a>
<div class="mc-mobile-section-label">AGENTS</div>
<a href="/hands" class="mc-mobile-link">HANDS</a>
<a href="/work-orders/queue" class="mc-mobile-link">WORK ORDERS</a>
<a href="/self-modify/queue" class="mc-mobile-link">UPGRADES</a>
<a href="/self-coding" class="mc-mobile-link">SELF-CODING</a>
<div class="mc-mobile-section-label">SYSTEM</div>
<a href="/tools" class="mc-mobile-link">TOOLS</a>
<a href="/swarm/events" class="mc-mobile-link">EVENTS</a>
<a href="/router/status" class="mc-mobile-link">ROUTER</a>
<a href="/grok/status" class="mc-mobile-link">GROK</a>
<div class="mc-mobile-section-label">COMMERCE</div>
<a href="/lightning/ledger" class="mc-mobile-link">LEDGER</a>
<a href="/creative/ui" class="mc-mobile-link">CREATIVE</a>
<a href="/voice/button" class="mc-mobile-link">VOICE</a>
<a href="/mobile" class="mc-mobile-link">MOBILE</a>
@@ -128,6 +139,25 @@
a.classList.add('active');
}
});
// Desktop "More" dropdown toggle
var dropdownToggle = document.querySelector('.mc-dropdown-toggle');
if (dropdownToggle) {
dropdownToggle.addEventListener('click', function(e) {
e.stopPropagation();
var dd = this.closest('.mc-nav-dropdown');
var isOpen = dd.classList.toggle('open');
this.setAttribute('aria-expanded', isOpen);
});
document.addEventListener('click', function() {
var dd = document.querySelector('.mc-nav-dropdown');
if (dd) {
dd.classList.remove('open');
var btn = dd.querySelector('.mc-dropdown-toggle');
if (btn) btn.setAttribute('aria-expanded', 'false');
}
});
}
</script>
<script src="/static/notifications.js"></script>
</body>