Lucy kennt sich: Selbst-Inventur, Karten-Gutachter, Lern-Klick, Radar->Queue
User-Auftrag 12.07. ("bau das alles ein im Sinne der Autonomie. Auch muss
Lucy selbst bemerken wenn sich etwas aendert"):
- deploy/selbst-inventur.sh (Cron 03:35): SELBST-STECKBRIEF wird LIVE aus
der Realitaet GENERIERT (Versionen, Dienste, Timer, Modelle, Crons,
Queue, Karten -> ~/.hermes/state/selbst-steckbrief.md) statt von Hand
gepflegt. Diff gegen gestern => die Box BEMERKT SELBST Aenderungen an
sich (stille Chronik-Karte + kurze Telegram-Zeile; sonst still).
- deploy/karten-gutachter.sh (Cron 04:00): stempelt jede neue Karte mit
"Empfehlung: ANNEHMEN/ABLEHNEN/UNKLAR + ein ehrlicher Satz" (prueft
Redundanz gegen den Steckbrief, Risiko, Nutzen; Ein-Schuss-Richter
gpt-oss/GLM; Stempel = Meinung, kein Gate; neuer Commit entwertet den
alten Stempel). Backend mischt den Stempel in /api/auftragsbuch,
UI zeigt ihn farbig auf der Karte.
- Ablehnen mit Grund (Lern-Klick): UI-Inline-Feld beim Ablehnen ->
/srv/models/mc2-ablehnungen.jsonl -> Idle-Radar bekommt die Gruende
als "NIE wieder vorschlagen"-Material vorgelegt.
- idle-radar-feed.sh: Material + Raster erweitert um Selbst-Steckbrief,
Inventur-Diff und Ablehn-Gruende (Fehlerfall 12.07.: redundanter
Gateway-Restart-Vorschlag waere damit gestorben).
- werkstatt-SOUL Regel 7 + wartung-Skill: REALITAETS-CHECK - existiert es
schon? Dann kanban_block statt Branch (ein Branch fuer Ueberfluessiges
ist ein gescheiterter Lauf).
- hermes-release-radar-feed.sh: echter Treffer legt zusaetzlich EINE rohe
Idee in die Queue (idempotent je Release-Tag) - der Blick nach draussen
fuettert denselben Kreislauf.
- deploy.sh: kopiert beide neuen Skripte; Doku AUFTRAGSBUCH.md +
wissen/OFFENE-FAEDEN.md; frontend/dist frisch gebaut (tsc+vite gruen,
UI gegen Live-Box verifiziert).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
+1
-1
File diff suppressed because one or more lines are too long
-1
File diff suppressed because one or more lines are too long
+1
File diff suppressed because one or more lines are too long
+53
-53
File diff suppressed because one or more lines are too long
Vendored
+2
-2
@@ -7,8 +7,8 @@
|
||||
<link rel="manifest" href="/manifest.webmanifest" />
|
||||
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
|
||||
<title>Mission Control 2.0</title>
|
||||
<script type="module" crossorigin src="/assets/index-ZY9WIA27.js"></script>
|
||||
<link rel="stylesheet" crossorigin href="/assets/index-Be3zHdDf.css">
|
||||
<script type="module" crossorigin src="/assets/index-D-87Dv5s.js"></script>
|
||||
<link rel="stylesheet" crossorigin href="/assets/index-Cq3xDDcK.css">
|
||||
</head>
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
|
||||
@@ -462,6 +462,8 @@ export interface AuftragItem {
|
||||
files_truncated: boolean
|
||||
frontend_ohne_build: boolean // Frontend-Quelltext ohne dist → Box kann nicht bauen
|
||||
status: AuftragStatus | null
|
||||
// Empfehlungs-Stempel des nächtlichen Karten-Gutachters (Meinung, kein Gate).
|
||||
empfehlung: { empfehlung: "ANNEHMEN" | "ABLEHNEN" | "UNKLAR"; satz: string; richter: string } | null
|
||||
}
|
||||
export interface SkillKandidat {
|
||||
file: string
|
||||
|
||||
@@ -283,6 +283,10 @@ function ProposalCard({
|
||||
const [open, setOpen] = useState(false)
|
||||
const [diff, setDiff] = useState<string | null>(null)
|
||||
const [diffLoading, setDiffLoading] = useState(false)
|
||||
// Ablehnen mit Grund: der Grund ist das Lern-Gedächtnis des Kreislaufs (Radar/Specifier
|
||||
// bekommen ihn als „nie wieder vorschlagen"-Material) — deshalb ein kleines Inline-Feld.
|
||||
const [rejecting, setRejecting] = useState(false)
|
||||
const [grund, setGrund] = useState("")
|
||||
const running = item.status?.state === "laeuft" || item.status?.state === "rollback"
|
||||
const decided = item.status?.state === "eingespielt"
|
||||
// Lucy-Karten (Desktop-App): Annehmen baut am PC (dist) statt auf der Box zu deployen.
|
||||
@@ -326,6 +330,16 @@ function ProposalCard({
|
||||
</div>
|
||||
<p className="mt-1.5 text-sm font-semibold text-foreground">{item.subject || "(ohne Titel)"}</p>
|
||||
{item.body && <p className="mt-1 whitespace-pre-wrap text-xs leading-relaxed text-muted-foreground line-clamp-6">{item.body}</p>}
|
||||
{item.empfehlung && (
|
||||
<p className={cn("mt-1.5 flex items-start gap-1.5 rounded-lg border px-2 py-1 text-[11px]",
|
||||
item.empfehlung.empfehlung === "ANNEHMEN" && "border-emerald-500/30 bg-emerald-500/5 text-emerald-300",
|
||||
item.empfehlung.empfehlung === "ABLEHNEN" && "border-red-500/30 bg-red-500/5 text-red-300",
|
||||
item.empfehlung.empfehlung === "UNKLAR" && "border-amber-500/30 bg-amber-500/5 text-amber-300")}
|
||||
title={`Nächtliches Kurz-Gutachten (${item.empfehlung.richter}) — eine Meinung, kein Muss. Die Entscheidung bleibt bei dir.`}>
|
||||
<Sparkles className="mt-0.5 h-3 w-3 shrink-0" />
|
||||
<span><b>Empfehlung: {item.empfehlung.empfehlung === "ANNEHMEN" ? "Annehmen" : item.empfehlung.empfehlung === "ABLEHNEN" ? "Ablehnen" : "Unklar"}.</b> {item.empfehlung.satz}</span>
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{!running && !decided && (
|
||||
@@ -343,10 +357,7 @@ function ProposalCard({
|
||||
</button>
|
||||
<button
|
||||
disabled={!!busy["ab:" + k]}
|
||||
onClick={() =>
|
||||
showConfirm("Vorschlag ablehnen?",
|
||||
`Der Branch '${item.branch}' wird auf Gitea gelöscht. Die Arbeit bleibt in der Historie, aber die Karte verschwindet.`,
|
||||
() => onAct("ab:" + k, "/api/auftragsbuch/ablehnen", { branch: item.branch, repo }))}
|
||||
onClick={() => setRejecting((v) => !v)}
|
||||
className="flex h-9 items-center gap-1.5 rounded-lg border border-red-500/30 bg-red-500/5 px-3 text-[11px] font-bold uppercase tracking-wide text-red-300/90 transition-all hover:bg-red-500/15 cursor-pointer disabled:opacity-50">
|
||||
{busy["ab:" + k] ? <Loader2 className="h-3.5 w-3.5 animate-spin" /> : <X className="h-3.5 w-3.5" />} Ablehnen
|
||||
</button>
|
||||
@@ -354,6 +365,38 @@ function ProposalCard({
|
||||
)}
|
||||
</div>
|
||||
|
||||
{rejecting && !running && !decided && (
|
||||
<div className="space-y-2 rounded-xl border border-red-500/25 bg-red-500/5 p-3">
|
||||
<p className="text-[11px] text-muted-foreground">
|
||||
Der Branch '{item.branch}' wird auf Gitea gelöscht (die Arbeit bleibt in der Historie).
|
||||
Sag der Box kurz <b>warum</b> — dann schlägt sie dir so etwas nicht wieder vor.
|
||||
</p>
|
||||
<input
|
||||
value={grund}
|
||||
onChange={(e) => setGrund(e.target.value)}
|
||||
placeholder={"Grund (optional) — z. B. „gibt es schon“ oder „will ich nicht“"}
|
||||
maxLength={400}
|
||||
className="w-full rounded-lg border border-border/60 bg-background/60 px-2.5 py-1.5 text-xs text-foreground placeholder:text-muted-foreground/50 focus:border-red-400/50 focus:outline-none"
|
||||
/>
|
||||
<div className="flex items-center gap-2">
|
||||
<button
|
||||
disabled={!!busy["ab:" + k]}
|
||||
onClick={() => {
|
||||
setRejecting(false)
|
||||
onAct("ab:" + k, "/api/auftragsbuch/ablehnen", { branch: item.branch, repo, grund: grund.trim() })
|
||||
}}
|
||||
className="flex h-8 items-center gap-1.5 rounded-lg border border-red-500/40 bg-red-500/10 px-3 text-[11px] font-bold uppercase tracking-wide text-red-300 transition-all hover:bg-red-500/20 cursor-pointer disabled:opacity-50">
|
||||
<X className="h-3.5 w-3.5" /> Wirklich ablehnen
|
||||
</button>
|
||||
<button
|
||||
onClick={() => setRejecting(false)}
|
||||
className="flex h-8 items-center rounded-lg border border-border/60 bg-background/40 px-3 text-[11px] font-semibold text-muted-foreground transition-all hover:text-foreground cursor-pointer">
|
||||
Abbrechen
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{item.status && (running || !decided) && item.status.detail && (
|
||||
<p className="text-[11px] text-muted-foreground">{item.status.detail}</p>
|
||||
)}
|
||||
|
||||
Reference in New Issue
Block a user