diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index f99db76..6cb9cd7 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -19,6 +19,7 @@ reverse. ## 2026-08-27 +- Creating Taiga tickets from nodes recorded as planned: a story per node from the node window, sub-packages as tasks picked in a dialog, through a backend proxy — the ticket ref comes back as the node id - The unassigned pill in the people bar shows each language's short form now — German `N.N.`, English `TBD` — with the long wording in the tooltip - `pull-doc --open` opens the fetched document in IntelliJ IDEA afterwards — also when nothing changed - A dead live session now reconnects by itself — unsent text stays in the editor and goes out as a diff once the server answers again diff --git a/docs/DECISIONS.md b/docs/DECISIONS.md index f0c1c8c..23d974e 100644 --- a/docs/DECISIONS.md +++ b/docs/DECISIONS.md @@ -7821,3 +7821,70 @@ neben der Langform: **Die Pille zeigt das Kürzel, Tooltip und `aria-label` die Langform** — ein Kürzel, das nirgends aufgelöst wird, wäre nur ein Rätsel. Nachgemessen im Browser: de-Pille „N.N." mit Tooltip „ohne Zuständigen: 29 % …", en-Pille „TBD" mit „unassigned: 29% …". + +## D91 — Ticket-Anlage aus Knoten: Proxy, zwei Aktionen, Häkchen-Dialog — festgelegt, noch nicht gebaut +Evaluation, wie aus einem Werkbaum-Knoten ein Taiga-Ticket wird; entschieden +als Multiple-Choice-Runde, festgehalten als Plan-Knoten (`#trk.create`), +**bewusst ungebaut** — dieselbe Form wie beim Nutzen-Feature (SPEC §11). + +**Der Zuschnitt folgt der Kanban-Ansicht, die tatsächlich benutzt wird:** +Dort sind **Stories die Karten**, Tasks stecken darin — und „storyless +tasks" existieren in Taiga zwar (die API erlaubt Tasks ohne `user_story`), +sind aber nur im Sprint-Taskboard in einer Extra-Sektion sichtbar, im Kanban +gar nicht. Also: **eine Story je Knoten, Tasks nur unter einem Knoten, der +schon eine Story trägt.** Taigas Hierarchie endet bei Epic → Story → Task +(Tasks haben keine Subtasks) — die Abbildung eines beliebig tiefen +Werkbaum-Baums ist damit immer ein Schnitt von höchstens drei Ebenen. Das +ist Arbeitsteilung, kein Verlust: Taiga trackt die grobe Arbeit, Werkbaum +die feine Zerlegung. Ein **Epic-Modus** (Knoten als Epic, Kinder als +Stories, Enkel als Tasks) ist erwogen und **zurückgestellt** — ein zweiter +Modus mit eigener UI-Frage; wer ein Epic braucht, wartet auf eine eigene +Entscheidung. + +**Zugang: Backend-Proxy mit schmalen, benannten Endpunkten** (auth, +projects, userstories, tasks) — kein transparenter Durchreich-Proxy: Der +wäre ein offenes Relay auf die Taiga-Instanz und bräche den +API-First-Stil des Backends (D76). Die **Taiga-Basis-URL ist +Server-Konfiguration** (env, wie das Master-Passwort, D77), nie +Request-Parameter — die SSRF-Falle naiver Proxies. Das **Token bleibt im +Browser**, der Proxy reicht nur den Authorization-Header durch; das Backend +speichert nichts und braucht kein Konten-Modell (serverseitige Tokens wären +die Ausbaustufe nach dem Owner-Passwort). Der Auth-Endpunkt sieht das +Passwort einmal im Durchflug — Request-Bodies dort nie loggen. +`/api/v1/info` meldet das Feature (`taiga`), der Knopf erscheint nur, wo +ein konfiguriertes Backend antwortet — dasselbe Muster wie die +Teilen-Lebendprobe (D81-Nachtrag). Verworfen: **direkt aus der App** +(hängt am CORS der Taiga-Instanz — messbar, aber jederzeit durch eine +Server-Einstellung kündbar) und **CLI zuerst** (kann später dieselben +Proxy-Endpunkte nutzen). Nebengewinn des Proxys: Der spätere +Status-Rückweg braucht zwingend eine Server-Seite — Taiga-**Webhooks** +erreichen keinen Browser. + +**Zwei Aktionen im Knoten-Fenster:** „Story anlegen" und „Story + +Teilpakete als Tasks". Die zweite zeigt einen **Häkchen-Dialog** über die +direkten Kinder (Nutzer-Entscheidung — maximale Kontrolle statt einer +stillen Regel): **vorbelegt** sind Pflicht- und optionale (`+`) Kinder +sowie die **realisierten** Alternativen einer `|`/`=`-Gruppe (ist nichts +realisiert, ist die Wahl nicht getroffen — nichts vorbelegt); verworfene +(`[-]`) erscheinen gar nicht, **erledigte (`[x]`/`[^]`) starten +abgewählt** — dort ist nichts mehr zu tun, und angelegt wird ohnehin alles +offen (Status-Übernahme ist das spätere Sync-Kapitel, `#trk.write`). + +**Das Projekt wird bei jeder Anlage gefragt** (Nutzer-Entscheidung): +Projektliste des eingeloggten Nutzers, nichts gemerkt — kein veralteter +Merker, ein Klick mehr je Ticket. Verworfen: fest im Backend (alle Pläne +in einem Projekt), je Dokument gemerkt, im Notationstext (bräuchte eine +SPEC-Festlegung). + +**Die Ticket-Ref wird als Knoten-ID zurückgeschrieben** — undo-fähig über +die vorhandene Maschinerie (D53/D63), für Story und Tasks gleichermaßen +(Taiga vergibt Refs projektweit über alle Typen). Das ist wörtlich die +reservierte Ticket-Referenz aus SPEC §11 („oft ist die Ticket-Kennung die +natürliche Knoten-ID", D34) und zugleich der Idempotenz-Marker: Ein Knoten, +der schon eine Ref trägt, wird nicht erneut angelegt. + +**Offen bis zum Bauen:** die zwei Messfragen an die echte Instanz — +Auth-Typ (`normal`/`ldap`, das LDAP-Plugin nutzt einen eigenen `type`) und +das Ref-Muster für die Tracker-Erkennung (`#123` vs. `#US-123`, +D34-Nachtrag). An der Notation ändert sich nichts; `llms.md` bleibt +unberührt. diff --git a/docs/examples/werkbaum.werkbaum b/docs/examples/werkbaum.werkbaum index 8e01b49..0417d71 100644 --- a/docs/examples/werkbaum.werkbaum +++ b/docs/examples/werkbaum.werkbaum @@ -199,11 +199,17 @@ | [?] #idea.drift.fixtures: Both parsers test against the same fixtures (S) | [?] #idea.drift.js: Run the one JS parser inside the IDE (M) - [?] #trk: Tracker integration (XL) %% exactly one of these, hence = - = [?] #trk.taiga: Taiga (L) https://taiga.io + = [?] #trk.taiga: Taiga (XL) https://taiga.io - [ ] #trk.resolve: Resolve "#123" over the REST API (M) - [ ] #trk.resolve.read: Read title, link and status (S) - [ ] #trk.resolve.map: Map the workflow onto the states (S) - [?] #trk.write: Write the status back (M) :#trk.resolve + - [ ] #trk.create: Create tickets from nodes (L) + - [ ] #trk.create.proxy: Backend proxy with named endpoints (M) + - [ ] #trk.create.login: Log in to Taiga, token stays in the browser (S) :#trk.create.proxy + - [ ] #trk.create.story: A "create story" action in the node window (S) :#trk.create.login + - [ ] #trk.create.tasks: Sub-packages become tasks, picked in a dialog (M) :#trk.create.story + - [ ] #trk.create.ref: The ticket ref becomes the node id (S) :#trk.create.story - [-] #trk.plugin: A plugin inside Taiga itself (L) %% legacy frontend, self-hosted only = [?] #trk.tenzu: Tenzu, the successor (XXL) https://tenzu.net = [ ] #trk.none: No tracker, plain text is enough (XS) @@ -1144,6 +1150,40 @@ The other direction: change a status in the plan and the ticket follows. Only worth doing once reading it is trustworthy. +#trk.create + One click in the node window turns a node into a Taiga ticket. The cut + follows the Kanban view actually in use: stories are the cards and tasks + live inside them — so a story per node, and tasks only under a node that + already carries a story. Decisions in D91. + +#trk.create.proxy + A few named endpoints in the Werkbaum backend (auth, projects, user + stories, tasks) relay to the Taiga instance configured server-side. No + CORS trouble and no open relay — the target URL is never a request + parameter — and /api/v1/info announces the feature, so the button only + appears where a backend answers. + +#trk.create.login + Taiga's token API behind the proxy: the token lives in the browser and is + only passed through, the backend stores nothing. The password crosses the + proxy once at login and is never logged. + +#trk.create.story + "Create story" in the node window: pick the project from your list — asked + on every creation, so nothing stale is remembered — and the story is + created. A node that already carries a ticket ref is left alone. + +#trk.create.tasks + The second action also creates the direct sub-packages as tasks under the + story, chosen in a checkbox dialog: preselected are required and optional + children plus the realized alternatives of a |/= group; discarded ones + never appear, finished ones start unchecked. + +#trk.create.ref + The returned ref (#123, #US-123) is written back into the line as the node + id, undoable — the reserved ticket reference of SPEC §11, and the marker + that keeps a second click from creating the ticket twice. + #trk.plugin Rejected: a plugin inside Taiga means its legacy frontend and works only in self-hosted installations. A companion application beside it is the better