IDE-Intake: Tiefe-Wahl (simpel/gruendlich) statt einem IDE-Knopf

Drei Modus-Knoepfe: Box · IDE: simpel · IDE: gruendlich. Die Wahl fliesst als
'AUFWAND (vom Commander gewaehlt): SIMPEL/GRUENDLICH'-Zeile in den Auftrag und
UEBERSTIMMT die Groessen-Selbsteinschaetzung von konzept-fliessband (simpel=2
Rollen/1 Runde, gruendlich=volle Kaskade). Skill: bei echter Ambition zur vollen
Tiefe (nicht 'im Zweifel kleiner'), explizite Commander-Wahl schlaegt Schaetzung.
Auto-Skalierung bleibt Fallback fuer Telegram/Lucy (ohne Knopf).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Hitonabi
2026-07-20 17:04:19 +02:00
parent efca31c21f
commit cfcb5d2db5
28 changed files with 143 additions and 98 deletions
+2 -1
View File
@@ -13,6 +13,7 @@ class IdeeIn(BaseModel):
titel: str
notiz: str = ""
welt: str = "box" # "box" = Werkstatt baut · "ide" = vorbereiten für Zed
tiefe: str = "" # nur ide: "simpel" | "gruendlich" (leer = Box schätzt selbst)
class TaskIn(BaseModel):
@@ -31,7 +32,7 @@ def list_queue() -> dict:
@router.post("/ideen")
def add_idea(body: IdeeIn) -> dict:
res = ideen.add_idea(body.titel, body.notiz, welt=body.welt)
res = ideen.add_idea(body.titel, body.notiz, welt=body.welt, tiefe=body.tiefe)
if not res.get("ok"):
raise HTTPException(400, res.get("error", "Idee konnte nicht angelegt werden."))
return res
+29 -6
View File
@@ -275,7 +275,20 @@ def list_queue() -> dict:
return data
def add_idea(titel: str, notiz: str = "", created_by: str = "mc2-ui", welt: str = "box") -> dict:
# Aufwand-Text je Tiefe, den der Commander per Knopf waehlt — wird dem Auftrag
# vorangestellt und ueberstimmt die Groessen-Selbsteinschaetzung von konzept-fliessband.
_AUFWAND = {
"simpel": ("AUFWAND (vom Commander gewaehlt): SIMPEL — nutze die KLEINE konzept-fliessband-"
"Variante: 2 Fach-Rollen, 1 Denk-Runde + 1 Haertetest, frueh raus bei WASSERDICHT. "
"Das ueberstimmt deine eigene Groessen-Einschaetzung."),
"gruendlich": ("AUFWAND (vom Commander gewaehlt): GRUENDLICH — nutze die VOLLE konzept-fliessband-"
"Kaskade: 3-5 Fach-Rollen, bis 3 Denk-Runden, mehrfacher Advocatus-Diaboli-Haertetest. "
"Das ueberstimmt deine eigene Groessen-Einschaetzung, auch wenn das Projekt klein wirkt."),
}
def add_idea(titel: str, notiz: str = "", created_by: str = "mc2-ui",
welt: str = "box", tiefe: str = "") -> dict:
"""Idee in die Queue legen.
welt='box' (Default): triage — der Specifier arbeitet sie aus, die Werkstatt baut,
@@ -284,18 +297,23 @@ def add_idea(titel: str, notiz: str = "", created_by: str = "mc2-ui", welt: str
die Box härtet ein Konzept (konzept-fliessband), legt ein leeres Gitea-Repo an und
füllt es NUR mit Doku. Ergebnis ist ein vorbereitetes Projekt, das der Commander in
Zed selbst baut (IDE-Welt). Bewusst KEIN --triage: so fasst der Auto-Zerleger die
Karte nie an und die Werkstatt baut sie nie (der Fan-out-Fehler vom 20.07.)."""
Karte nie an und die Werkstatt baut sie nie (der Fan-out-Fehler vom 20.07.).
tiefe (nur ide): 'simpel' | 'gruendlich' — vom Knopf gewaehlte Konzept-Tiefe; leer =
die Box schaetzt selbst (Fallback fuer Telegram/Lucy)."""
if not _available():
return {"ok": False, "error": "Die Ideen-Queue lebt auf der Box."}
titel = (titel or "").strip()
if not (3 <= len(titel) <= 200):
return {"ok": False, "error": "Titel bitte zwischen 3 und 200 Zeichen."}
welt = "ide" if str(welt).lower() == "ide" else "box"
notiz = (notiz or "").strip()
if welt == "ide":
args = ["create", titel, "--assignee", "projektstart", "--created-by", created_by, "--json"]
aufwand = _AUFWAND.get(str(tiefe).lower())
if aufwand:
notiz = aufwand + ("\n\n" + notiz if notiz else "")
else:
args = ["create", titel, "--triage", "--created-by", created_by, "--json"]
notiz = (notiz or "").strip()
if notiz:
args[2:2] = ["--body", notiz[:4000]]
try:
@@ -611,9 +629,14 @@ def konzept_of(task_id: str) -> dict:
d = _parse_json(r.stdout)
except Exception:
return {"available": True, "error": "Karte nicht lesbar."}
haystack = ""
# `kanban show --json` legt die Task-Felder verschachtelt unter "task" ab und
# liefert die jüngste Abschluss-Zusammenfassung als "latest_summary".
haystack, task_status = "", None
if isinstance(d, dict):
haystack = str(d.get("result") or "") + "\n" + str(d.get("body") or "")
t = d.get("task") if isinstance(d.get("task"), dict) else d
task_status = t.get("status")
haystack = "\n".join(str(x or "") for x in (
t.get("result"), t.get("body"), d.get("latest_summary")))
for ev in d.get("events") or []:
if isinstance(ev, dict) and ev.get("kind") == "completed":
haystack += "\n" + str((ev.get("payload") or {}).get("summary") or "")
@@ -645,7 +668,7 @@ def konzept_of(task_id: str) -> dict:
data = {"available": True, "repo": repo, "clone_url": clone_url,
"datei": used_name, "konzept": konzept[:60000]}
# Fertige Konzepte ändern sich selten → cachen (aber nur, wenn die Karte done ist).
if isinstance(d, dict) and d.get("status") == "done":
if task_status == "done":
if len(_konzept_cache) >= 20:
_konzept_cache.pop(next(iter(_konzept_cache)))
_konzept_cache[task_id] = data