feat(taiga): Status zurückschreiben — zwei Knöpfe, niemand gewinnt von selbst (D91-Nachtrag 8, SPEC §9)
Weicht der Ticket-Status von der Statusbox ab, markiert das Knoten-Fenster
die Abweichung und bietet beide Richtungen ausdrücklich an.
- „nach Taiga schreiben": Spalte des Projekts suchen (Taiga schreibt nach Id,
die Namen sind je Projekt frei) und mit der zuletzt GELESENEN `version`
patchen — hat jemand dazwischen geändert, lehnt Taiga ab und der Text steht
im Fenster, statt dass etwas überschrieben wird.
- „aus Taiga übernehmen": `setStatusBox()` schreibt die Box in die Textzeile,
undo-fähig wie jede andere Änderung.
- Schreibbar sind nur die fünf abgebildeten Zustände; `[?]`, `[!]`, `[-]` und
der neutrale Knoten lassen das Ticket unangetastet — mit Begründung im
Fenster.
- Proxy: zwei Spaltenlisten (`/taiga/{userstory,task}-statuses?slug=`) und
zwei Schreib-Endpunkte (`PATCH …/{ref}/status?slug=`); die Zielspalte wählt
der Editor, das Backend parst die Notation nicht (D14).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Fable 5
parent
4e6f98bbee
commit
323e8fe0ba
@@ -19,6 +19,10 @@ reverse.
|
||||
|
||||
## 2026-08-27
|
||||
|
||||
- Where a ticket and the plan disagree, the node window says so and offers both directions as explicit buttons — nothing happens by itself
|
||||
- "Write to Taiga" sets the ticket's column from the status box; only the five mapped states can be written, `[?]`, `[!]`, `[-]` and a box-less node leave the ticket untouched
|
||||
- Writing goes against the state you were shown: if someone changed the ticket meanwhile, it is refused instead of overwritten, and ↻ fetches the new state
|
||||
- "Take from Taiga" writes the status box into the text line — an ordinary, undoable change, visible to everyone in a shared document
|
||||
- The node window shows the state of a ticket now: subject, status and assignee, read from Taiga through the backend proxy
|
||||
- Taiga's status is shown next to the notation's own box (`In progress → [~]`) — a column name outside the five known ones stays unmapped and is shown as text
|
||||
- Reading a ticket never changes the plan: no status is written back, and the text stays untouched
|
||||
|
||||
@@ -8261,3 +8261,73 @@ den Namen — die Spaltennamen sind je Projekt frei, der Proxy braucht also
|
||||
Und ein `PATCH` verlangt die `version` des Tickets (optimistisches Sperren):
|
||||
Hat jemand dazwischen geändert, meldet sich der Konflikt, statt ihn zu
|
||||
überschreiben — dieselbe Haltung wie beim Live-Editing (D76).
|
||||
|
||||
**Nachtrag 8 — `#trk.write` gebaut: die Entscheidungen des Baus
|
||||
(2026-08-27).** Die Festlegung aus Nachtrag 7 ist umgesetzt (SPEC §9). Was
|
||||
dabei zu entscheiden war:
|
||||
|
||||
**Taiga schreibt nach Status-Id, die Auswahl trifft der Editor.** Die
|
||||
Spaltennamen sind je Projekt frei, `PATCH` nimmt die **Id**. Der Proxy
|
||||
bekommt deshalb zwei Lese-Endpunkte für die Spalten
|
||||
(`GET /taiga/userstory-statuses`, `…/task-statuses`, je `?slug=`), und die
|
||||
Zuordnung Statusbox → Spaltenname bleibt im Editor (`taigaStatusName`,
|
||||
`pickStatus`, headless) — dieselbe Grenze wie beim Lesen: Statuscodes sind
|
||||
Notation, das Backend parst sie nicht (D14). Verglichen wird mit **derselben
|
||||
Normalisierung** wie beim Lesen (Groß-/Kleinschreibung und Leerraum egal),
|
||||
damit nicht zwei Stellen dieselbe Regel unterschiedlich auslegen. Findet sich
|
||||
die Spalte im Projekt nicht, wird **nicht geschrieben** und das Fenster nennt
|
||||
sie beim Namen.
|
||||
|
||||
**Geschrieben wird gegen die gelesene `version`** — Taigas optimistische
|
||||
Sperre. Der Client schickt die Version, die er im Fenster **gezeigt** hat;
|
||||
hat jemand dazwischen etwas geändert, lehnt Taiga ab, der Fehlertext steht im
|
||||
Fenster und der ↻-Knopf holt den neuen Stand. Damit kann das Zurückschreiben
|
||||
nie eine fremde Änderung überschreiben, die man gar nicht gesehen hat — die
|
||||
Haltung des Live-Editings (D76), hier gegenüber einem fremden System.
|
||||
Nachgemessen: Ein zweiter Schreibversuch mit der alten Version wird abgelehnt
|
||||
(HTTP 400 samt Taigas Meldung), danach ↻ und derselbe Knopf gelingen.
|
||||
|
||||
**Die Ref bleibt die Adresse, auch beim Schreiben.** `PATCH
|
||||
/taiga/userstories/{ref}/status?slug=` löst intern erst den Slug und dann die
|
||||
Ref auf (drei Umläufe je Schreibvorgang). Der Endpunkt könnte die Ticket-**Id**
|
||||
nehmen — die kennt der Client aus dem Lesen —, aber dann hieße `{…}` im selben
|
||||
Pfadmuster einmal Ref und einmal Id: eine Zweideutigkeit, die man später
|
||||
einmal falsch liest. Schreibvorgänge sind einzelne Klicks; der Umlauf ist
|
||||
billiger als die Verwechslung.
|
||||
|
||||
**Die Statusbox setzt `setStatusBox()` in parser.js** — Text→Text neben
|
||||
`setFoldMark` (D38) und `expandShortIds` (D55), also an der Stelle, an der die
|
||||
Zeilenstruktur ohnehin bekannt ist: Angefasst wird nur die Box, Einrückung,
|
||||
Zeichen, **beide** Faltmarken-Stellungen und Label bleiben zeichengenau
|
||||
stehen. Geschrieben wird über `replaceTextUndoable` (D53) — ein Undo-Schritt,
|
||||
nie `src.value =`.
|
||||
|
||||
**Nachgemessen** im Browser gegen das lokal laufende Backend mit Taiga-Stub:
|
||||
Ticket „In progress → [~]" gegen Plan `[ ]` zeigt die Abweichung in der
|
||||
Warnfarbe und beide Knöpfe; *nach Taiga schreiben* setzt die Spalte „New",
|
||||
danach ist die Abweichung weg **und der Notationstext unverändert**; ein
|
||||
`[?]`-Knoten bekommt **nur** den Übernehmen-Knopf samt Begründung, und
|
||||
*übernehmen* schreibt `[/]` in die Zeile (`- [/] API-Teil #T-1234`), worauf
|
||||
der Neubau das Fenster schließt. Backend: 5 neue Client-Tests (Spaltenlisten
|
||||
je Typ, PATCH per Id mit Status und Version, Konflikt-Durchreichung, gelesene
|
||||
Version) und 2 Ende-zu-Ende-Tests; Frontend 596 Tests (11 neue). Gegenproben:
|
||||
Normalisierung der Spaltensuche entfernt → genau die eine danach benannte
|
||||
Zusicherung fällt; die Faltmarken-Gruppe aus `setStatusBox` entfernt → die
|
||||
vier Statusbox-Tests.
|
||||
|
||||
**Zwei Werkzeugfallen, beide schon einmal bezahlt und wieder zugeschnappt:**
|
||||
Der Stub muss **chunked** Bodies lesen — der JDK-HttpClient sendet ohne
|
||||
`Content-Length`, und wer nur die Länge liest, sieht `{}` und meldet einen
|
||||
Versionskonflikt, den es nicht gibt (dieselbe Falle wie in Nachtrag 4). Und
|
||||
`execCommand('insertText')` braucht **Fensterfokus**: Ohne dargestellte
|
||||
Browser-Fläche tut es nichts, der Prüftext muss dann über `value` plus
|
||||
`input`-Ereignis gesetzt werden (dieselbe Sorte Grenze wie D57).
|
||||
|
||||
**Dazu eine neue, die es vorher nicht gab:** Ein **gerades** `"` in einem
|
||||
deutschen i18n-Text (`„{name}"`) beendet die JS-Zeichenkette — der Bundle war
|
||||
kaputt, und **`npm test` merkte davon nichts**, weil die Testsuite `app.js`
|
||||
nie importiert (sie prüft die Module). Gesehen hat es erst der Dev-Server.
|
||||
Wer i18n-Texte mit Anführungszeichen schreibt, nimmt die typografischen
|
||||
(`„…“`, `«…»`, `“…”`) und prüft die Datei einmal mit `npx esbuild src/app.js
|
||||
--outfile=…` — das ist die schnellste ehrliche Syntaxprobe für eine Datei,
|
||||
die kein Test anfasst.
|
||||
|
||||
+27
-27
@@ -500,9 +500,25 @@ es einen gibt, den **Zuständigen**.
|
||||
„In progress“ `[~]`, „Ready for test“ `[/]`, „Done“ `[x]`, „Archived“ `[^]`;
|
||||
Groß-/Kleinschreibung und Leerraum sind egal. Ein Name außerhalb dieser
|
||||
Liste bleibt **unabgebildet** und steht nur als Text — geraten wird nicht.
|
||||
- **Gelesen, nie geschrieben.** Der Notationstext bleibt unangetastet; die
|
||||
Statusbox des Knotens ändert sich nicht, und die Abbildung sagt nichts über
|
||||
Fortschritt (§4) oder Kosten (§5). Das Zurückschreiben ist reserviert (§11).
|
||||
- **Von selbst geschieht nichts.** Weder wird der Notationstext angefasst
|
||||
noch das Ticket: Die Abbildung ist eine Anzeige, keine Aussage über
|
||||
Fortschritt (§4) oder Kosten (§5).
|
||||
- **Weicht der Ticket-Status von der Statusbox ab**, wird das **markiert**
|
||||
(Warnfarbe, mit der eigenen Box daneben) und mit **zwei ausdrücklichen
|
||||
Aktionen** angeboten:
|
||||
- *nach Taiga schreiben* — setzt den Status des Tickets auf die Spalte, die
|
||||
zur eigenen Statusbox gehört. Angeboten nur für die fünf abgebildeten
|
||||
Zustände; `[?]`, `[!]`, `[-]` und der neutrale Knoten haben keine
|
||||
Entsprechung und lassen das Ticket **unangetastet** — das Fenster sagt,
|
||||
warum. Geschrieben wird gegen den zuletzt **gelesenen** Stand: Hat jemand
|
||||
inzwischen etwas geändert, wird abgelehnt statt überschrieben, und die
|
||||
Meldung steht im Fenster (↻ holt den neuen Stand).
|
||||
- *aus Taiga übernehmen* — schreibt die Statusbox in die **Textzeile**, als
|
||||
gewöhnliche, undo-fähige Änderung; in einem geteilten Dokument (§9,
|
||||
`?live=`) sehen sie damit alle. Der Neubau schließt das Fenster; die neue
|
||||
Farbe des Knotens ist die Rückmeldung.
|
||||
- Beide Richtungen betreffen **einen** Knoten; eine Sammelaktion über einen
|
||||
Teilbaum gibt es nicht.
|
||||
- Geholt wird erst, wenn das Fenster **kurz stehen bleibt** (nicht im
|
||||
Vorüberfahren), und je Ticket **einmal je Sitzung** — ein ↻-Knopf im Fenster
|
||||
holt neu. Ohne Anmeldung an der Instanz, ohne Projekt-Zuordnung oder ohne
|
||||
@@ -511,7 +527,7 @@ es einen gibt, den **Zuständigen**.
|
||||
- Reine Bedienhilfe wie das Fenster selbst: nicht im Grafikexport, nicht im
|
||||
Druck.
|
||||
|
||||
Siehe D91-Nachtrag 6.
|
||||
Siehe D91-Nachträge 6, 7 und 8.
|
||||
|
||||
**Die Knotenfarbe zeigt den effektiven Status (§4)**, nicht den intrinsischen —
|
||||
das Diagramm beantwortet „wie weit ist das wirklich?“. Wo der eigene Status
|
||||
@@ -1385,33 +1401,17 @@ Taiga-Projekte, benennt das Schlagwort `&taiga.<slug>` (unten) das Projekt
|
||||
je Teilbaum — eine Ref wird gegen das Projekt des nächsten Vorfahren mit so
|
||||
einem Tag aufgelöst. Siehe D91-Nachträge 2 und 3.
|
||||
Der **Stand** eines so bezeichneten Tickets (Betreff, Status, Zuständiger)
|
||||
wird im Knoten-Fenster gezeigt (§9) — gelesen, nie geschrieben; das
|
||||
**Zurückschreiben** des Status bleibt reserviert (unten).
|
||||
wird im Knoten-Fenster gezeigt (§9); weicht er von der Statusbox ab, sind
|
||||
dort beide Richtungen als ausdrückliche Aktionen zu haben — von selbst
|
||||
geschieht nichts.
|
||||
Freie Schlagworte liegen **nicht** mehr auf `#` — siehe `&tag` unten; damit
|
||||
ist die frühere Dreifach-Rolle von `#` aufgelöst (D34).
|
||||
|
||||
### Status zurückschreiben — festgelegt, noch nicht gebaut
|
||||
### Status zurückschreiben
|
||||
|
||||
Der Ticket-Stand wird gelesen (§9). Der Abgleich in die andere Richtung ist
|
||||
entschieden, aber ungebaut; die Regeln stehen hier, bevor sie gebaut werden.
|
||||
|
||||
- **Niemand gewinnt von selbst.** Weicht der gelesene Ticket-Status von der
|
||||
Statusbox des Knotens ab, wird die Abweichung im Knoten-Fenster
|
||||
**markiert** und mit **zwei ausdrücklichen Aktionen** angeboten: *nach
|
||||
Taiga schreiben* (Statusbox → Ticket) und *aus Taiga übernehmen*
|
||||
(Ticket-Status → Statusbox). Kein Takt, kein stiller Abgleich, keine
|
||||
Richtung, die die andere überstimmt.
|
||||
- **Übernehmen ist eine gewöhnliche Textänderung**: Die Statusbox wird
|
||||
geschrieben wie beim Falten (§9) — undo-fähig, in einem geteilten Dokument
|
||||
für alle sichtbar. Sonst gilt weiter, dass der Text die Quelle der Wahrheit
|
||||
ist und kein Werkzeug ihn ungefragt umschreibt.
|
||||
- **Geschrieben wird nur, was die Abbildung kennt** (§9, die fünf Zustände).
|
||||
`[?]`, `[!]`, `[-]` und der neutrale Knoten haben keine Entsprechung und
|
||||
lassen das Ticket **unangetastet**; das Fenster sagt, warum. Erfunden wird
|
||||
nichts — dieselbe Haltung wie beim Lesen, wo ein unbekannter Spaltenname
|
||||
unabgebildet bleibt.
|
||||
- Beide Richtungen betreffen **einen** Knoten. Eine Sammelaktion über einen
|
||||
Teilbaum ist offen und wird, wenn sie kommt, hier festgelegt.
|
||||
**Umgesetzt** — Anzeige und Regeln in §9 (Abweichung im Knoten-Fenster, zwei
|
||||
ausdrückliche Aktionen, nur die abgebildeten Zustände). Entscheidung und
|
||||
verworfene Alternativen: D91-Nachträge 7 und 8.
|
||||
|
||||
### Schlagworte (`&tag`)
|
||||
|
||||
|
||||
@@ -204,7 +204,7 @@
|
||||
- [/] #trk.resolve: Resolve "#US-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
|
||||
- [x] #trk.write: Write the status back (M) :#trk.resolve
|
||||
- [^] #trk.create: Create tickets from nodes (XL)
|
||||
- [^] #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
|
||||
@@ -1158,8 +1158,9 @@
|
||||
shown as plain text; making the mapping configurable is still open.
|
||||
|
||||
#trk.write
|
||||
The other direction: change a status in the plan and the ticket follows.
|
||||
Only worth doing once reading it is trustworthy.
|
||||
The other direction. Neither side wins by itself: a difference between the
|
||||
ticket and the status box is marked, and both directions are offered as
|
||||
explicit buttons — writing only for the five mapped states.
|
||||
|
||||
#trk.create
|
||||
One click in the node window turns a node into a Taiga ticket. The cut
|
||||
|
||||
Reference in New Issue
Block a user