Karten-Titel: kurz und an der Wortgrenze statt abgeschnittener Textwueste

Der Commander schreibt seine Ideen roh — ein ganzer Absatz wurde 1:1 zum
Kartentitel und war danach ueberall abgeschnitten ("... und lass uns ueberlegen
wie"). Drei Stellen:

- add_idea(): Zurufe ueber 90 Zeichen bekommen einen kurzen Anzeige-Titel
  (erster Satz, sonst Schnitt an der Wortgrenze). Der VOLLE Wortlaut wandert in
  den Auftrag, mit Kopfzeile "der Kartentitel ist nur eine Kurzfassung" — sonst
  arbeitet der Worker am abgeschnittenen Satz.
- _projekt_titel(): der Gruppenname nahm stur die aelteste Karte und schnitt sie
  bei 80 Zeichen mitten im Wort ab. Jetzt gewinnt bei fehlendem gemeinsamen
  Praefix der kuerzeste Mitglieds-Titel (die aus einem Konzept abgeleiteten
  Karten sind die gepflegten) — Praefixe wie "Idee:" fallen weg, Schnitt an der
  Wortgrenze. Aus der ARM-Kette wird so "ARM-UI — Modernes Dashboard fuer
  Automatic Ripping Machine" statt des halben Zurufs.
- UI: Tooltip der Zeile zeigt den vollen TITEL statt des Auftragstexts (der
  beginnt mit Vorspann-Bausteinen und half beim Lesen nie).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Hitonabi
2026-07-21 08:46:40 +02:00
parent 3cdd5cdd10
commit 94dc3fba9b
25 changed files with 72 additions and 31 deletions
+42 -3
View File
@@ -99,9 +99,27 @@ def _blocker_frage(task_id: str) -> dict | None:
return {"frage": frage[:800], "kind": kind}
def _kurz(text: str, max_len: int = 80) -> str:
"""Auf max_len kürzen — aber an der WORTGRENZE, nicht mitten im Wort (User 21.07.:
„Idee: Recherchiere zu ARM … und lass uns überlegen wie")."""
t = " ".join((text or "").split())
if len(t) <= max_len:
return t
schnitt = t[:max_len].rsplit(" ", 1)[0].rstrip(" ,;:-")
return (schnitt or t[:max_len]) + ""
_TITEL_PRAEFIX_RX = re.compile(r"^\s*(idee|projekt|bitte)\s*[:\-]?\s*", re.I)
def _projekt_titel(titles: list[str]) -> str:
"""Anzeigename einer Karten-Familie: gemeinsamer Titel-Präfix (z. B. „Homelab-Dashboard 2.0"),
sonst der Titel der ältesten Karte. Reine Anzeige-Heuristik, nichts hängt davon ab."""
sonst der AUFGERÄUMTESTE Mitglieds-Titel. Reine Anzeige-Heuristik, nichts hängt davon ab.
Der kürzeste Titel gewinnt, weil die gepflegten Karten (aus einem Konzept abgeleitet)
kurz sind und der rohe Zuruf des Commanders lang — vorher nahm die Gruppe stur die
älteste Karte und schnitt deren Textwüste bei 80 Zeichen mitten im Wort ab.
"""
if not titles:
return "Projekt"
prefix = titles[0]
@@ -109,7 +127,9 @@ def _projekt_titel(titles: list[str]) -> str:
while prefix and not t.startswith(prefix):
prefix = prefix[:-1]
prefix = prefix.strip(" -:·")
return prefix if len(prefix) >= 10 else titles[0][:80]
if len(prefix) >= 10:
return _kurz(prefix)
return _kurz(_TITEL_PRAEFIX_RX.sub("", min(titles, key=len)))
def _ketten_anreichern(items: list[dict]) -> list[dict]:
@@ -319,6 +339,12 @@ _ZIEL_HINWEIS = ("Richte den Tech-Vorschlag an der gewaehlten ZIEL-PLATTFORM aus
"nicht am Industrie-Default.")
# Auftrags-Art „Idee pruefen": Produkt-Blick statt Umsetzungs-Blick, KEIN Projektstart.
# Langer Zuruf → kurzer Kartentitel; der volle Wortlaut steht dann hier im Auftrag.
# Der Worker MUSS wissen, dass der Titel nur eine Kurzfassung ist, sonst arbeitet er
# am abgeschnittenen Satz statt an der ganzen Idee.
_WORTLAUT_KOPF = ("DIE IDEE IM WORTLAUT (der Kartentitel ist nur eine Kurzfassung fuer die "
"Anzeige — DAS hier ist der Auftrag):\n")
# Die erste Zeile ist zugleich der MARKER, an dem die Queue-Sicht die Art wiedererkennt
# (das Kanban kennt kein eigenes Feld dafuer) — beim Aendern _ART_MARKER mitziehen.
_ART_MARKER = "AUFTRAGS-ART: IDEE PRUEFEN"
@@ -377,6 +403,15 @@ def add_idea(titel: str, notiz: str = "", created_by: str = "mc2-ui",
return {"ok": False, "error": "Titel bitte zwischen 3 und 200 Zeichen."}
welt = "ide" if str(welt).lower() == "ide" else "box"
notiz = (notiz or "").strip()
# Der Commander schreibt roh — sein Zuruf ist oft ein ganzer Absatz und wurde bisher
# komplett zum Kartentitel (in jeder Liste abgeschnitten, unlesbar). Also: kurzer
# Titel für die Anzeige, der WORTLAUT wandert vollständig in den Auftrag, damit der
# Worker nichts verliert.
wortlaut = ""
if len(titel) > 90:
satz = re.split(r"(?<=[.!?])\s", titel)[0].strip()
wortlaut = titel
titel = satz if 10 <= len(satz) <= 90 else _kurz(titel, 80)
if welt == "ide":
args = ["create", titel, "--assignee", "projektstart", "--created-by", created_by, "--json"]
# Vorspann: Aufwand (Tiefe) + Ziel-Plattform + Infrastruktur — überstimmt die
@@ -393,9 +428,13 @@ def add_idea(titel: str, notiz: str = "", created_by: str = "mc2-ui",
infra = _INFRA_DOCKER if str(ziel).lower() == "docker" else _INFRA_LXC
vorspann.append(infra + " " + _ZIEL_HINWEIS
+ (" " + _ZIEL_EHRLICH if zielwahl else ""))
if wortlaut:
vorspann.insert(0, _WORTLAUT_KOPF + wortlaut)
notiz = "\n\n".join(vorspann) + ("\n\n" + notiz if notiz else "")
else:
args = ["create", titel, "--triage", "--created-by", created_by, "--json"]
if wortlaut:
notiz = _WORTLAUT_KOPF + wortlaut + ("\n\n" + notiz if notiz else "")
if notiz:
args[2:2] = ["--body", notiz[:4000]]
try:
@@ -916,7 +955,7 @@ def projekt_aus_idee(task_id: str, ziel: str = "", tiefe: str = "") -> dict:
_list_cache["ts"] = 0.0
try:
from services import announce
announce.add(f"Aus der Idee „{quell_titel}“ wird ein Projekt: {task['id']} — die Box "
announce.add(f"Aus der Idee „{_kurz(quell_titel, 60)}“ wird ein Projekt: {task['id']} — die Box "
"bereitet das Repo vor, gebaut wird in Zed.",
"[Ideen-Queue]", "ideen-queue", "silent")
except Exception: