Entwurf · Issue 37ed1fb

Ein Sprint in der Adresse

Entwurf zu Issue 37ed1fb, zu docs/plans/37ed1fb-a-sprint-in-the-address.md. Die Tabellen unten rufen parseCycleFilter, inBoardView und cycleSummaries beim Rendern auf. Was fehlte, steht als Text da — ein Zustand, den der Code verlassen hat, lässt sich nicht mehr rechnen, nur erinnern. Was gilt, wird gerechnet.

Das vorige Kapitel hat die fertige Karte dem Sprint zugeschlagen, der sie geschlossen hat. Das war richtig, und es ist ohne Tür ausgeliefert worden: die vier Karten, die S17 abgeschlossen hat, waren am Morgen des 24. August vom laufenden Board verschwunden, und es gab keine Adresse, unter der sie standen.

Die Frage dieses Kapitels ist nicht, wie man eine Zeile anklickbar macht, sondern welche Auflösung eine Board-Adresse hat.

01Was fehlte

Ein Filter lebt in der Adresse, weil ein Filter, den man nicht verlinken kann, aus einer Seite ein Programm macht — das ist die Regel überall hier. parseCycleFilter las dafür ^(\d{4})-(\d{1,2})$. Eine Hälfte kam in diesem Muster nicht vor, und was nicht passte, war kein Fehler, sondern kein Filter: wer ?cycle=2026-09.1 tippte, landete nicht auf einem Sprint-Board und auch nicht auf einer Fehlermeldung, sondern auf genau dem Board, das er verlassen wollte.

Die Leiste hatte dieselbe Lücke, und sie sagte es offen: über den Sprintzeilen stand der Kommentar, der ganze Zyklus sei eine Adresse und die Hälften seien nicht adressierbar, deshalb seien sie Text und keine Links, die aussähen wie Griffe und nirgendwohin führten. Der Satz beschrieb den Code richtig. Er hatte nur eine Annahme darüber getroffen, was ein Leser braucht, und die ist am Tag der Auslieferung falsch geworden.

Zwischen „alles Offene plus das, was der laufende Sprint geschlossen hat“ und „der ganze Zyklus“ lag keine Stufe — nicht weil ein Sprint-Board dasselbe zeigte wie ein Zyklus-Board, sondern weil es das Sprint-Board nicht gab.

02Ein Wert, zwei Auflösungen

Aufgemacht wird das an genau einer Stelle. Die Grammatik ist schon da: cycle/2026-09.1 ist die Art, wie jede Karte ihre eigene Lage buchstabiert. Ein Filter mit optionaler Hälfte liest dieselbe Schreibweise und bedeutet ohne Hälfte weiterhin genau das, was jede bestehende Adresse vorher bedeutet hat:

Adressegelesen alszurückgeschrieben
ohne ?cycle=kein Filter — der laufende Sprint—
?cycle=2026-09Zyklus 9, 2026 — beide Hälften2026-09
?cycle=2026-09.1Sprint 17, 20262026-09.1
  • Ein Wert, zwei Auflösungen — nicht ?cycle=2026-09&sprint=1. Zwei unabhängige Parameter können auseinanderlaufen; einer kann es nicht. Die dritte Spalte ist der Rückweg: filterKey schreibt, was parseCycleFilter liest, damit Leistenlinks und Query nicht zwei Konventionen werden.
  • Optional, nicht Pflichtfeld mit null — wer eine Zyklusfrage stellt, soll nicht „und kein Sprint“ dazusagen müssen.
  • inCycle lernt nichts über Sprints. Es beantwortet die Zyklusfrage, sein Kommentar sagt das, und das bleibt wahr. Die Verengung eines Boards ist die Aufgabe von inBoardView.

Dieselben Karten gegen die drei Adressen:

Kartegeplant inohne ?cycle=?cycle=2026-09?cycle=2026-09.1
Kontoblockgeschlossen in S17, der ersten Hälfte des laufenden Zykluscycle/2026-09.1—steht auf dem Boardsteht auf dem Board
SED-PROBEgeschlossen in S17cycle/2026-09.1—steht auf dem Boardsteht auf dem Board
Board zeigt eine Seitegeschlossen im laufenden Sprint S18cycle/2026-09.2steht auf dem Boardsteht auf dem Board—
Leser prüfen Signaturenoffen, geplant für S17 — und deshalb heute übergetragencycle/2026-09.1steht auf dem Boardsteht auf dem Board—

03Warum ein Sprint-Board auch offene Karten verengt

Auf dem Board ohne ?cycle= werden offen und geschlossen gegenläufig gelesen: was offen ist, zeigt nach vorn und bleibt sichtbar; was geschlossen ist, ist das Protokoll eines Zeitraums und gehört in seinen. Diese Asymmetrie bleibt, wo sie hingehört.

Ein Sprint in der Adresse ist eine andere Frage. Wer nach S17 fragt, fragt rückwärts, auch über die offene Arbeit — „was war dieser Zeitraum“ ist nicht „was ist noch nicht fertig“. Ein Sprint-Board verengt deshalb beide Kartenarten auf seine Hälfte. Die letzte Zeile der Tabelle oben zeigt genau das: die offene Karte war für S17 geplant, steht heute aber in S18 — auf dem Sprint-Board von S17 ist sie nicht, auf dem laufenden Board sehr wohl, denn dort lautet die Frage wirklich: was liegt auf meinem Tisch.

04Jede Zeile beschreibt das Board, das sie öffnet

Das Issue meldete einen Widerspruch: die Leiste sagte 4✓, die Spalte Done war leer. Keine der beiden Zahlen war falsch — die Zeile zählte einen Zyklus, das Board ohne Filter ist ein Sprint. Es fehlte die Zeile für das Board, auf dem der Leser stand. Der Meilenstein, der die Regel ausgeliefert hat, hatte behauptet, Zeile und Spalte könnten nicht auseinandergehen; für ein gefiltertes Board stimmt das, für das ungefilterte war es nie geprüft.

Die Regel, die den Widerspruch auflöst, ist eine und nicht drei Korrekturen: jede Zeile beschreibt das Board, das sie öffnet. Die beiden rechten Spalten werden von verschiedenem Code gerechnet — links cycleSummaries, rechts inBoardView — und ein Paar gleicher Zahlen ist deshalb ein Beleg und keine Behauptung:

Zeileöffnetzählt fertigKarten in Closed
C9?cycle=2026-093✓3
S17?cycle=2026-09.12✓2
S18?cycle=2026-09.21✓1

Die leere Closed-Spalte des laufenden Boards steht damit unter einer Zeile, die null sagt, und die fertige Arbeit der zwei Wochen davor steht eine Zeile darüber, unter ihrer eigenen Adresse.

Daraus folgt eine zweite Änderung an der Leiste: die Hälften werden auch unter dem Zyklus gezeigt, der gerade betrachtet wird, nicht nur unter dem laufenden. Die bisherige Begründung — anderswo sei eine Hälfte bloß ein Plan, der sich meist in „3 Karten“ und „0 Karten“ teilt — gilt für einen Zyklus, den niemand aufgeschlagen hat. Wer auf ?cycle=2026-08 steht, hat nach diesem Zyklus gefragt, und seine zwei Sprints sind die Struktur der Antwort.