Ampel / ampel (push) Failing after 24s
Die Vorschlags-Inbox (routers/services auftragsbuch, AuftragsbuchView, /auftraege, Cockpit- Kachel + Handlungsbedarf-Eintrag, deploy/auftrag-annehmen.sh, lucy-annahme.sh, bagatell- annahme.sh, docs/AUFTRAGSBUCH.md) war seit 02.08.2026 ungenutzt: Kanban-Tools seit 21.08. aus, v3-Umbau lief ueber Branch -> Ampel -> Merge -> deploy.sh, PC-Executor-IP in Box und Config veraltet. Doku behauptete den Karten-Weg trotzdem als Standard. Jetzt: - Cockpit: Kachel "Ideen" (offen/haengend) statt "Auftragsbuch"; Handlungsbedarf zeigt haengende Ideen und springt in die Ideen-Ansicht. - Guide/Schaubild: Kreislauf Idee -> Ideen-Queue -> IDE am PC -> Ampel -> DEIN Merge -> Live. - events.py: kein Auftragsbuch-Fingerprint mehr; config.py: PC_EXECUTOR_URL-Default auf .22. - Skills/Skripte: selbst-inventur ohne Karten-Abschnitt, morning-report nennt offene Branches, konzept-fliessband verweist auf die Ideen-Ansicht. - Doku: STACK "Deploy & Pipeline" = Branch -> Ampel -> User-Merge -> deploy.sh (einziger Weg), GRENZEN/ARBEITSWEISE/FALLEN/README/BEDIENUNG/RUNBOOK/GEMINI_BRIEFING/gitea-workflow angepasst, ZIELBILD Punkt 9, OFFENE-FAEDEN + RAPHAEL.md: erledigt. - Bewusst geblieben: werkstatt-SOUL/projektstart-SOUL (Werkstatt-Persona, eigener Faden), Chronik-Kategorie "auftragsbuch" fuer historische Eintraege. Auf der Box bereits erledigt (Hand): mc2-bagatell.timer deaktiviert + Units archiviert, PC-Executor-URL in ~/.hermes/config.yaml auf .22. Baut auf wartung/lucy-stimme-proxy-raphael auf (Proxy /api/lucy/stimme/*). Gates: eslint 0 Fehler, vitest 59/59, tsc + vite build gruen, frontend/dist committet, py_compile gruen. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
202 lines
7.9 KiB
TypeScript
202 lines
7.9 KiB
TypeScript
import { lazy } from "react"
|
|
import {
|
|
createRootRoute,
|
|
createRoute,
|
|
createRouter,
|
|
redirect,
|
|
useNavigate,
|
|
} from "@tanstack/react-router"
|
|
import { Shell } from "@/App"
|
|
import { NAV } from "@/nav"
|
|
import { RouteFehler } from "@/components/RouteFehler"
|
|
import { CockpitView } from "@/views/cockpit/CockpitView"
|
|
|
|
// Routenbaum (v3-Umbau P2).
|
|
//
|
|
// Bewusst code-based statt dateibasiert: Es sind zwölf Routen, und sie stehen dadurch
|
|
// alle an EINER Stelle — inklusive dem, was in den Suchparametern erlaubt ist. Eine
|
|
// Datei-Konvention würde diese Übersicht auf zwölf Dateien verteilen, ohne dass jemand
|
|
// etwas davon hätte.
|
|
//
|
|
// Code-Splitting bleibt exakt wie bisher: die Views hängen an React.lazy, das Cockpit als
|
|
// Startseite lädt eifrig. Kein Chunk verschiebt sich durch den Router.
|
|
const ModelsView = lazy(() => import("@/views/ModelsView").then((m) => ({ default: m.ModelsView })))
|
|
const ConnectView = lazy(() => import("@/views/ConnectView").then((m) => ({ default: m.ConnectView })))
|
|
const AgentView = lazy(() => import("@/views/AgentView").then((m) => ({ default: m.AgentView })))
|
|
const KonsoleView = lazy(() => import("@/views/KonsoleView").then((m) => ({ default: m.KonsoleView })))
|
|
const GuideView = lazy(() => import("@/views/GuideView").then((m) => ({ default: m.GuideView })))
|
|
const IdeenView = lazy(() => import("@/features/ideen/IdeenAnsicht").then((m) => ({ default: m.IdeenView })))
|
|
const SkillsView = lazy(() => import("@/views/SkillsView").then((m) => ({ default: m.SkillsView })))
|
|
const ChronikView = lazy(() => import("@/views/ChronikView").then((m) => ({ default: m.ChronikView })))
|
|
const WissenView = lazy(() => import("@/views/WissenView").then((m) => ({ default: m.WissenView })))
|
|
|
|
// ── Suchparameter ───────────────────────────────────────────────────────────
|
|
// Regel für das, was hier steht: Alles, was ein Kollege per Link teilen können soll und
|
|
// was einen Reload überleben muss. Nichts Flüchtiges (Hover, Entwürfe im Eingabefeld).
|
|
|
|
/** Wurzel: die System-Schublade. Gehört hierher, weil sie über JEDER Ansicht liegt. */
|
|
export interface WurzelSuche {
|
|
system?: "wartung" | "logs"
|
|
}
|
|
|
|
const wurzelSuche = (roh: Record<string, unknown>): WurzelSuche => {
|
|
const s = roh.system
|
|
return s === "wartung" || s === "logs" ? { system: s } : {}
|
|
}
|
|
|
|
export const rootRoute = createRootRoute({
|
|
component: Shell,
|
|
validateSearch: wurzelSuche,
|
|
notFoundComponent: () => (
|
|
<RouteFehler
|
|
titel="Diese Seite gibt es nicht"
|
|
text="Der Link zeigt auf eine Ansicht, die Mission Control nicht kennt. Vielleicht stammt er aus einer älteren Fassung."
|
|
/>
|
|
),
|
|
})
|
|
|
|
// Wurzelpfad → Cockpit. `redirect` statt einer Kopie der Startseite: sonst gäbe es
|
|
// zwei URLs für denselben Inhalt und die aktive Markierung in der Sidebar flackert.
|
|
const indexRoute = createRoute({
|
|
getParentRoute: () => rootRoute,
|
|
path: "/",
|
|
beforeLoad: () => {
|
|
throw redirect({ to: "/cockpit" })
|
|
},
|
|
})
|
|
|
|
/** Eine Route je Ansicht — kurze Schreibweise, weil sich nur Pfad und Bauteil ändern.
|
|
* Generisch über den Pfad, damit TanStack Router den Literal-Typ behält: mit
|
|
* `path: string` verlöre `navigate({ to: "/cockpit" })` seine Typprüfung. */
|
|
function ansicht<P extends string>(path: P, component: () => React.ReactNode) {
|
|
return createRoute({
|
|
getParentRoute: () => rootRoute,
|
|
path,
|
|
component,
|
|
// Fehlergrenze PRO Route (Befund B-13): Vorher hing eine einzige Grenze in main.tsx
|
|
// um die ganze App — ein Renderfehler in den Modellen nahm Cockpit, Konsole und
|
|
// Chronik mit. Jetzt bleiben Schiene, Kopfzeile und Statusanzeige stehen.
|
|
errorComponent: ({ error, reset }) => (
|
|
<RouteFehler
|
|
titel="Diese Ansicht ist abgestürzt"
|
|
text="Der Rest von Mission Control läuft weiter. Erneut versuchen — oder in eine andere Ansicht wechseln."
|
|
fehler={error}
|
|
onErneut={reset}
|
|
/>
|
|
),
|
|
})
|
|
}
|
|
|
|
// Das Cockpit verlinkt in andere Bereiche und bekam den Sprung bisher als Prop von der
|
|
// App gereicht. Jetzt kommt er aus dem Router — die Ansicht selbst bleibt unverändert.
|
|
function CockpitSeite() {
|
|
const navigate = useNavigate()
|
|
return <CockpitView onNavigate={(v) => {
|
|
const ziel = NAV.find((n) => n.id === v)
|
|
if (ziel) navigate({ to: ziel.pfad })
|
|
}} />
|
|
}
|
|
|
|
const cockpitRoute = ansicht("/cockpit", () => <CockpitSeite />)
|
|
const skillsRoute = ansicht("/skills", () => <SkillsView />)
|
|
/** Chronik: `?q=` filtert den Verlauf — Ziel der „Finden"-Einträge der Palette (P6). */
|
|
export interface ChronikSuche {
|
|
q?: string
|
|
}
|
|
const chronikRoute = createRoute({
|
|
getParentRoute: () => rootRoute,
|
|
path: "/chronik",
|
|
validateSearch: (roh: Record<string, unknown>): ChronikSuche =>
|
|
typeof roh.q === "string" && roh.q ? { q: roh.q } : {},
|
|
component: () => <ChronikView />,
|
|
errorComponent: ({ error, reset }) => (
|
|
<RouteFehler titel="Die Chronik ist abgestürzt" text="Der Rest läuft weiter." fehler={error} onErneut={reset} />
|
|
),
|
|
})
|
|
const verbindenRoute = ansicht("/verbinden", () => <ConnectView />)
|
|
const konsoleRoute = ansicht("/konsole", () => <KonsoleView />)
|
|
const anleitungRoute = ansicht("/anleitung", () => <GuideView />)
|
|
const agentRoute = ansicht("/agent", () => <AgentView />)
|
|
|
|
/** Ideen: `?offen=<id>` hält die aufgeklappte Karte fest (Befund B-05).
|
|
* Vorher lag das in `useState` — wer während eines laufenden Workers neu lud,
|
|
* verlor seinen Platz in der Liste. */
|
|
export interface IdeenSuche {
|
|
offen?: string
|
|
}
|
|
const ideenRoute = createRoute({
|
|
getParentRoute: () => rootRoute,
|
|
path: "/ideen",
|
|
validateSearch: (roh: Record<string, unknown>): IdeenSuche =>
|
|
typeof roh.offen === "string" && roh.offen ? { offen: roh.offen } : {},
|
|
component: () => <IdeenView />,
|
|
errorComponent: ({ error, reset }) => (
|
|
<RouteFehler titel="Die Ideen-Ansicht ist abgestürzt" text="Der Rest läuft weiter." fehler={error} onErneut={reset} />
|
|
),
|
|
})
|
|
|
|
/** Wissen: `?datei=` macht jede Notiz verlinkbar, `?suche=` trägt das Suchwort
|
|
* aus der Befehlspalette herein (P6). */
|
|
export interface WissenSuche {
|
|
datei?: string
|
|
suche?: string
|
|
}
|
|
const wissenRoute = createRoute({
|
|
getParentRoute: () => rootRoute,
|
|
path: "/wissen",
|
|
validateSearch: (roh: Record<string, unknown>): WissenSuche => ({
|
|
...(typeof roh.datei === "string" && roh.datei ? { datei: roh.datei } : {}),
|
|
...(typeof roh.suche === "string" && roh.suche ? { suche: roh.suche } : {}),
|
|
}),
|
|
component: () => <WissenView />,
|
|
errorComponent: ({ error, reset }) => (
|
|
<RouteFehler titel="Die Wissens-Ansicht ist abgestürzt" text="Der Rest läuft weiter." fehler={error} onErneut={reset} />
|
|
),
|
|
})
|
|
|
|
/** Modelle: `?reiter=` — der Reiter war lokaler State und damit nicht teilbar. */
|
|
export type ModellReiter = "werkbank" | "routing" | "discover"
|
|
export interface ModelleSuche {
|
|
reiter?: ModellReiter
|
|
}
|
|
const modelleRoute = createRoute({
|
|
getParentRoute: () => rootRoute,
|
|
path: "/modelle",
|
|
validateSearch: (roh: Record<string, unknown>): ModelleSuche => {
|
|
const r = roh.reiter
|
|
return r === "werkbank" || r === "routing" || r === "discover" ? { reiter: r } : {}
|
|
},
|
|
component: () => <ModelsView />,
|
|
errorComponent: ({ error, reset }) => (
|
|
<RouteFehler titel="Der Modell-Manager ist abgestürzt" text="Der Rest läuft weiter." fehler={error} onErneut={reset} />
|
|
),
|
|
})
|
|
|
|
export const routeTree = rootRoute.addChildren([
|
|
indexRoute,
|
|
cockpitRoute,
|
|
ideenRoute,
|
|
skillsRoute,
|
|
chronikRoute,
|
|
wissenRoute,
|
|
verbindenRoute,
|
|
konsoleRoute,
|
|
anleitungRoute,
|
|
modelleRoute,
|
|
agentRoute,
|
|
])
|
|
|
|
export const router = createRouter({
|
|
routeTree,
|
|
// Ein LAN-Gerät: der Verlauf muss nicht scrollen-restaurieren, aber die Position
|
|
// oben zu haben ist beim Ansichtswechsel richtig.
|
|
scrollRestoration: true,
|
|
defaultPreload: false,
|
|
})
|
|
|
|
declare module "@tanstack/react-router" {
|
|
interface Register {
|
|
router: typeof router
|
|
}
|
|
}
|