Annahme-Selbstheilung: Orphan-Branches entlarven, veraltete Branches auto-rebasen

Anlass: feature/cleanse-skill-index — ein Werkstatt-Worker hatte git init statt
Klonen gemacht (Orphan-Commit ohne gemeinsamen Vorfahren, Inhalt: ~/.hermes-Index-
Dateien statt Repo-Dateien). Solche Karten sahen aus wie normale Vorschlaege,
liefen bei Annahme aber IMMER in 'refusing to merge unrelated histories' — und
die Fehlermeldung ('bitte am PC aufloesen') war eine Sackgasse. Das passiert
oefter; darum drei Schichten dagegen:

1) ERKENNEN (Auftragsbuch): neue Karten-Flags 'verwaist' (kein merge-base mit
   main -> rote Markierung 'kein gemeinsamer Ursprung', Annehmen-Knopf fehlt,
   API+Runner halten zusaetzlich dicht) und 'leer' (Diff gegen main leer ->
   'bringt nichts'-Badge). Empfehlung auf der Karte: ablehnen mit Grund,
   Idee frisch in die Queue.

2) HEILEN (beide Runner, mc2 + lucy): kollidiert der Merge, weil main weiter-
   gelaufen ist, versucht der Runner automatisch einen Rebase des Branches auf
   main (isolierter Worktree; Gates laufen danach normal, Merge-Message sagt
   'auto-rebased'). Nur wenn auch der Rebase kollidiert, faellt die Karte durch —
   mit ehrlicher Meldung statt 'am PC aufloesen'.

3) VERHINDERN (werkstatt-SOUL): voll klonen (nie git init/--depth), Selbstcheck
   'git merge-base HEAD origin/main' + fetch/rebase vor JEDEM Push, nie
   ~/.hermes-Artefakte committen.

Doku: AUFTRAGSBUCH.md + FALLEN.md (Erkennungsmuster: Diff 0 Dateien + behind ~
ganze Historie) + OFFENE-FAEDEN. Geprueft: py_compile gruen, bash -n beide
Runner gruen, tsc+vite build gruen (dist dabei); merge-base-Verhalten am echten
kaputten Branch auf der Box verifiziert.
This commit is contained in:
Hitonabi
2026-07-13 09:34:23 +02:00
parent 9872c1b3f6
commit 552299a761
12 changed files with 158 additions and 40 deletions
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+1 -1
View File
@@ -7,7 +7,7 @@
<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-D-87Dv5s.js"></script>
<script type="module" crossorigin src="/assets/index-DyMSyf_4.js"></script>
<link rel="stylesheet" crossorigin href="/assets/index-Cq3xDDcK.css">
</head>
<body>
+2
View File
@@ -461,6 +461,8 @@ export interface AuftragItem {
files: { status: string; path: string }[]
files_truncated: boolean
frontend_ohne_build: boolean // Frontend-Quelltext ohne dist → Box kann nicht bauen
verwaist?: boolean // kein gemeinsamer Ursprung mit main (kaputt aufgesetzt) → Merge unmöglich
leer?: boolean // Diff gegen main ist leer → bringt nichts (schon in main / am Repo vorbei)
status: AuftragStatus | null
// Empfehlungs-Stempel des nächtlichen Karten-Gutachters (Meinung, kein Gate).
empfehlung: { empfehlung: "ANNEHMEN" | "ABLEHNEN" | "UNKLAR"; satz: string; richter: string } | null
+22 -2
View File
@@ -344,6 +344,7 @@ function ProposalCard({
{!running && !decided && (
<div className="flex shrink-0 items-center gap-2">
{!item.verwaist && (
<button
disabled={!!busy[k]}
onClick={() =>
@@ -355,6 +356,7 @@ function ProposalCard({
className="flex h-9 items-center gap-1.5 rounded-lg border border-emerald-500/40 bg-emerald-500/10 px-3 text-[11px] font-bold uppercase tracking-wide text-emerald-300 transition-all hover:bg-emerald-500/20 cursor-pointer disabled:opacity-50">
{busy[k] ? <Loader2 className="h-3.5 w-3.5 animate-spin" /> : <Check className="h-3.5 w-3.5" />} Annehmen
</button>
)}
<button
disabled={!!busy["ab:" + k]}
onClick={() => setRejecting((v) => !v)}
@@ -401,11 +403,29 @@ function ProposalCard({
<p className="text-[11px] text-muted-foreground">{item.status.detail}</p>
)}
{item.verwaist && (
<p className="rounded-lg border border-red-500/30 bg-red-500/5 px-2 py-1.5 text-[11px] text-red-300/90">
<b>Kaputt aufgesetzt:</b> Dieser Branch hat keinen gemeinsamen Ursprung mit main (der Werkstatt-Worker
hat nicht richtig geklont) Annehmen ist technisch unmöglich, deshalb fehlt der Knopf.
Am besten <b>ablehnen mit Grund</b>; die Idee kann danach frisch in die Queue.
</p>
)}
<div className="flex flex-wrap items-center gap-2 text-[11px] text-muted-foreground">
<span className="rounded bg-background/40 px-1.5 py-0.5 font-mono">{item.ahead} Commit{item.ahead === 1 ? "" : "s"}</span>
{item.shortstat && <span className="rounded bg-background/40 px-1.5 py-0.5 font-mono">{item.shortstat.trim()}</span>}
{item.behind > 0 && (
<span className="flex items-center gap-1 rounded bg-amber-500/10 px-1.5 py-0.5 text-amber-300" title="main ist seit diesem Vorschlag weitergelaufen">
{item.verwaist && (
<span className="flex items-center gap-1 rounded bg-red-500/10 px-1.5 py-0.5 text-red-300" title="Kein gemeinsamer Vorfahre mit main — git kann diesen Branch niemals mergen.">
<AlertTriangle className="h-3 w-3" /> kein gemeinsamer Ursprung
</span>
)}
{item.leer && (
<span className="flex items-center gap-1 rounded bg-amber-500/10 px-1.5 py-0.5 text-amber-300" title="Der Diff gegen main ist leer — der Branch würde nichts verändern (Inhalt schon in main oder am Repo vorbei gearbeitet).">
<AlertTriangle className="h-3 w-3" /> Diff leer bringt nichts
</span>
)}
{item.behind > 0 && !item.verwaist && (
<span className="flex items-center gap-1 rounded bg-amber-500/10 px-1.5 py-0.5 text-amber-300" title="main ist seit diesem Vorschlag weitergelaufen — bei Konflikten versucht die Annahme automatisch einen Rebase.">
<AlertTriangle className="h-3 w-3" /> {item.behind} hinter main
</span>
)}