Single Source of Truth: Frontend/Backend-Ungereimtheiten ausgeraeumt (Review 15.07.)

Backend (Wahrheit reparieren):
- gateway_reachable() prueft jetzt den ECHTEN mc2-gateway (:9010 via V1_UPSTREAM)
  statt nur die Engine - toter Denkpfad war in Health/Cockpit unsichtbar
- /api/system/services vollstaendig: + LLM-Gateway, + Steuerpult, + Waechter
  (mc2-steward via is-active); scope-Feld (user/system); mc2-gateway/mc2-steward
  in die Restart-Allowlist
- Connect-Health Leitung 1 prueft den Client-Pfad (V1_UPSTREAM statt llama-swap)
- agent_status liefert pc_executor_url (UI zeigte hartcodierte IP/Ports)
- SSE-Endlosschleife gefixt: /api/auftragsbuch schrieb announce-branches bei JEDEM
  Aufruf neu -> mtime-Bump -> invalidate -> Refetch im 3-s-Takt je Client;
  jetzt nur noch bei echter Aenderung

Frontend (ein Datenweg):
- SystemDrawer komplett auf React-Query-Hooks (vorher eigenes 3-s-setInterval auf
  dieselben Endpunkte inkl. teurem Updates-Check); Dienste-Liste = Backend-Antwort
  (SERVICES-UI-Kopie geloescht); useDialog statt Eigenbau; fmtBytes statt Duplikat
- useLucyHealth: Dienst-Matching per systemd-unit statt Namensfragment; Gateway-
  Reparatur zielt auf mc2-gateway; neuer Waechter-Check; Backend-tot => ehrliches
  rotes Verdikt statt eingefrorener letzter Stand (auch Sidebar)
- Toter Code raus: views/models/Cockpit.tsx (908 Z.) + RoleAssignModal + Placeholder;
  LaneEditor (Routing-Policy) + WarmSetManager damit ZURUECK im UI als Tab
  "Routing & Warm-Set" im Modell-Manager
- Cockpit: blockierte Ideen-Karten erscheinen in "Braucht dich" + Kachel-Hint;
  Verbinden-Kachel zeigt 3 Leitungen live statt hartem "Zed"
- Maschinenraum: ehrliches "frei" (reale Belegung) + eigenes KV-Cache-Segment
- AgentView: Ports/PC-Adresse aus der API; Guide lehrt Lanes chat/coding
- queries.ts: useModels nutzt SSE-relax; Chronik-Limit im Query-Key;
  useJobs/useZeitmaschine mit enabled-Schalter

Verifiziert: tsc+vite gruen, Backend-Smoke beide Gateway-Modi, UI live gegen die
Box (Cockpit/Werkbank/Routing-Tab/Drawer rendern mit Echtdaten).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Hitonabi
2026-07-15 22:22:27 +02:00
parent b36561990e
commit 0c69c491e5
161 changed files with 1522 additions and 2458 deletions
+22 -14
View File
@@ -3,7 +3,6 @@ import { Command as CommandIcon, ChevronLeft, ChevronRight, Loader2, Menu, X } f
import { NAV, type ViewId } from "@/nav"
import { CommandPalette } from "@/components/CommandPalette"
import { ExpertToggle } from "@/components/ExpertToggle"
import { Placeholder } from "@/views/Placeholder"
import { SystemDrawer } from "@/components/SystemDrawer"
import { useHealth, useSystemStatus } from "@/lib/queries"
import type { Health, SystemStatus } from "@/lib/api"
@@ -54,7 +53,9 @@ export default function App() {
const [drawerOpen, setDrawerOpen] = useState(false)
const [drawerTab, setDrawerTab] = useState<"maintenance" | "logs">("maintenance")
const { data: health } = useHealth()
// isError mitnehmen: React Query behält bei Fehlern die letzten Daten — ohne den
// Fehler-Blick zeigte die Sidebar bei totem Backend ewig den letzten grünen Stand.
const { data: health, isError: backendDown } = useHealth()
const { data: sysStatus } = useSystemStatus()
useEffect(() => {
@@ -129,6 +130,7 @@ export default function App() {
}}
view={view}
health={health}
backendDown={backendDown}
sysStatus={sysStatus}
/>
</aside>
@@ -151,6 +153,7 @@ export default function App() {
onClose={() => setMobileNavOpen(false)}
view={view}
health={health}
backendDown={backendDown}
sysStatus={sysStatus}
/>
</aside>
@@ -208,9 +211,6 @@ export default function App() {
{view === "agent" && <AgentView />}
{view === "konsole" && <KonsoleView />}
{view === "guide" && <GuideView />}
{!["dashboard", "auftraege", "eigenleben", "models", "connect", "memory", "wissen", "chronik", "agent", "konsole", "guide"].includes(view) && (
<Placeholder title={active.label} hint={active.hint} />
)}
</Suspense>
</main>
</div>
@@ -221,13 +221,14 @@ export default function App() {
// Sidebar-Inhalt (Logo, Navigation, Status-Fuß) — geteilt zwischen Desktop-Leiste
// und Mobile-Off-Canvas, damit beide immer identisch bleiben.
function SidebarContent({
collapsed, onToggleCollapse, onClose, view, health, sysStatus,
collapsed, onToggleCollapse, onClose, view, health, backendDown, sysStatus,
}: {
collapsed: boolean
onToggleCollapse?: () => void
onClose?: () => void
view: ViewId
health: Health | undefined
backendDown: boolean
sysStatus: SystemStatus | undefined
}) {
return (
@@ -308,18 +309,25 @@ function SidebarContent({
<div className="flex justify-center">
<span className={cn(
"h-2.5 w-2.5 rounded-full ring-2 ring-black/40",
health
? (!health.engine_reachable ? "bg-amber-500"
backendDown || !health
? "bg-red-500"
: (!health.engine_reachable ? "bg-amber-500"
: (health.brain && !health.brain.ready ? "bg-amber-500 animate-pulse" : "bg-emerald-500 animate-pulse"))
: "bg-red-500"
)} title={health
? (!health.engine_reachable ? "Engine offline"
: (health.brain && !health.brain.ready ? `Hirn offline (${health.brain.model ?? "fast"})` : "Engine + Hirn online"))
: "Backend offline"} />
)} title={backendDown
? "Zentrale antwortet nicht — Anzeigen evtl. veraltet"
: health
? (!health.engine_reachable ? "Engine offline"
: (health.brain && !health.brain.ready ? `Hirn offline (${health.brain.model ?? "fast"})` : "Engine + Hirn online"))
: "Backend offline"} />
</div>
) : (
<div className="space-y-2 text-left">
{health ? (
{backendDown ? (
<span className="flex items-center gap-2 text-red-400">
<span className="h-2 w-2 rounded-full bg-red-500 animate-pulse" />
<span className="truncate">Zentrale antwortet nicht</span>
</span>
) : health ? (
<>
<span className="flex items-center gap-2">
<span className={cn("h-2 w-2 rounded-full animate-pulse", health.engine_reachable ? "bg-emerald-500" : "bg-amber-500")} />