feat: Größen-Konflikt — Teilpakete müssen zur angegebenen Größe passen (D62)
Größen zählen für diese eine Prüfung als Bereiche (Untergrenzen verdoppeln sich, XXL nach oben offen); Konflikt erst, wenn selbst die günstigste Lesart der Kinder die großzügigste des Elternknotens erreicht. Warnung sizeConflict an der Elternzeile, Badge in --warn, Grund in Tooltip und aria-label. Nichts wird automatisch korrigiert. Nebenbefund behoben: der Grafikexport zeichnete das Größen-Badge mit festen Farben — das invertierte implizite M stand seit jeher gefüllt im Bild. Die acht berechtigten Konflikte im eigenen Plan sind nachgezogen. 372 Tests, 16 neue. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Fable 5
parent
257e1a9c17
commit
5b37c117e1
@@ -19,6 +19,8 @@ reverse.
|
||||
|
||||
## 2026-08-24
|
||||
|
||||
- A size that cannot hold its sub-packages turns its badge amber and gets a warning — sizes are read as ranges, so only sure conflicts are reported
|
||||
- Fix: the exported SVG drew the assumed-M badge filled like a real size instead of inverted
|
||||
- Dependency links now run behind every node, highlighted ones included — no more lines struck through node titles
|
||||
- Fix: the line to an only child was torn where the child does not sit centred in its cell
|
||||
- An optional node (`+`) joins the cheapest path while it is being worked on — started work is the open front
|
||||
|
||||
@@ -4582,3 +4582,74 @@ heraus, fallen genau die fünf danach benannten Zusicherungen; nimmt man
|
||||
`chosenPool` heraus, genau die zwei zu den Alternativen. Die Tests, die das
|
||||
**unveränderte** Verhalten festhalten (unangetastete und erledigte Zugaben
|
||||
bleiben draußen), bleiben in beiden Fällen grün.
|
||||
|
||||
## D62 — Größen-Konflikt: Bereiche statt Punkt-Zahlen, XXL nach oben offen
|
||||
Gewünscht war eine Anzeige, „wenn die angegebenen T-Shirt-Größen mit den
|
||||
Unterknoten nicht zusammenpassen" — ohne automatische Korrektur. Die Skala
|
||||
dafür gab es schon (`SIZE_RANK`, D18 nutzt Rang+1 als Kosten); die eigentliche
|
||||
Entscheidung war, **wann** „passt nicht" gilt.
|
||||
|
||||
**Eine Punkt-Zahl je Größe trägt nicht — egal wie steil die Skala steigt.**
|
||||
Der erste Vorschlag war eine additive Verdopplungsskala (XS=1 … XXL=32):
|
||||
Summe der Kinder > Elternwert ⇒ Konflikt. Der Nutzer wandte ein, dass damit
|
||||
bei jedem größeren Baum die oberen Knoten selbst mit XXL nicht auskommen —
|
||||
und die Prüfung der Steilheit führte auf einen härteren Befund: Schon das
|
||||
**kanonische Beispiel aus SPEC §10** warnte. `Website-Relaunch (XL)` mit
|
||||
Kindern `(M) + (XL) + (M)` ergibt bei Faktor 2 wie bei Faktor 3 eine Summe
|
||||
über dem Elternwert — sobald ein Kind die Elterngröße teilt, sprengt jedes
|
||||
Geschwister die Summe. Das widerspricht der gelebten Praxis, dass „XL =
|
||||
XL-Kind plus etwas Kleinkram" eine völlig normale Schätzung ist. Der Fehler
|
||||
liegt nicht im Faktor, sondern darin, dass eine Punkt-Zahl so tut, als wäre
|
||||
`(M)` exakt.
|
||||
|
||||
**Entschieden: Größen sind Bereiche.** XS=[1,2), S=[2,4), M=[4,8), L=[8,16),
|
||||
XL=[16,32), **XXL=[32,∞)**. Konflikt erst, wenn die Summe der **Untergrenzen**
|
||||
der Kinder die **Obergrenze** des Elternknotens erreicht — also erst, wenn es
|
||||
unter *jeder* Lesart falsch ist. Das hält das kanonische Beispiel sauber
|
||||
(24 < 32), meldet vier `(S)` unter `(M)` (8 ≥ 8), enthält den Ordinal-Fall
|
||||
(ein strikt größeres Kind warnt immer) und beantwortet den Einwand direkt:
|
||||
**XXL hat keine Obergrenze**, ein XXL-Knoten warnt nie — für die großen
|
||||
Sammelknoten behauptet die Skala schlicht keine Schranke mehr. Das ist
|
||||
dieselbe Haltung wie bei `unknownStatus` und `descStray`: lieber laut, aber
|
||||
nur, wo es sicher ist. D46 („die Größen sind ordinal, nicht additiv") bleibt
|
||||
für alles andere unangetastet — die Bereichs-Lesart gilt genau dieser einen
|
||||
Prüfung.
|
||||
|
||||
**Was zählt:** nur die direkten Kinder (je Ebene eine eigene Prüfung, die
|
||||
Warnung zeigt auf die Elternzeile), davon nur die mit **angegebener** Größe —
|
||||
fehlende Größe ist keine Autoren-Aussage (D44-Linie; anders als bei den
|
||||
Pfadkosten wird kein M angenommen). Verworfene und optionale (`+`) Kinder
|
||||
zählen nicht; in einer disjunktiven Gruppe (`|`/`=`) zählt die **kleinste**
|
||||
Alternative, denn nur eine wird realisiert. Die Gegenrichtung (Eltern größer
|
||||
als die Kindersumme) warnt nicht — sie heißt nur unvollständige Zerlegung,
|
||||
und dafür gibt es den Geister-Knoten (D8). Ein Elternknoten ohne Größe wird
|
||||
nie geprüft.
|
||||
|
||||
**Darstellung: das Badge wechselt auf `--warn`, nicht auf Rot.** Der Nutzer
|
||||
hatte Rot vorgeschlagen und sich für die Warnfarbe entschieden: „Der Plan
|
||||
widerspricht sich" hat mit `#B45309` schon eine Farbe (Geister-Knoten,
|
||||
Warnzeilen), und Rot bliebe unvergeben (D34). Dazu die Warnung `sizeConflict`
|
||||
mit Zeilennummer (eine Stelle: `build()` in warnings.js), der Grund im
|
||||
Tooltip und im `aria-label`. **Nichts wird automatisch korrigiert.**
|
||||
|
||||
**Nebenbefund: Der Grafikexport zeichnete das Größen-Badge mit festen
|
||||
Farben** (`drawBadge(sizeEl, '#0F766E', '#ffffff')`) — das invertierte
|
||||
implizite M (D18) stand damit seit jeher gefüllt im exportierten Bild. Jetzt
|
||||
liest der Export die gemessenen Farben wie bei Tags und Diskrepanz-Marke;
|
||||
damit folgt auch das Konflikt-Badge von selbst.
|
||||
|
||||
**Der eigene Plan hatte acht solcher Konflikte** — alle berechtigt, die
|
||||
Größen stammten aus der Zeit ohne Prüfung. Nachgezogen (`#not` L→XL, `#ed`
|
||||
XL→XXL, `#ed.live`/`#ed.path`/`#ed.fold`/`#bld`/`#col.own` M→L, `#col.pad`
|
||||
S→L, und als Kaskade davon `#col` XL→XXL): 0 Warnungen. Dass die erste
|
||||
Anwendung der Regel den eigenen Plan korrigiert, ist kein schlechtes Zeichen —
|
||||
genau dafür ist sie da.
|
||||
|
||||
**Nachgemessen:** 372 Tests (16 neue in `tests/sizes.test.js`); Gegenprobe
|
||||
per Mutation — XXL-Obergrenze wieder eingeführt: genau die zwei danach
|
||||
benannten Zusicherungen fallen; optionale Kinder mitgezählt: genau eine;
|
||||
disjunktive Gruppen summiert statt Minimum: genau zwei. Im Browser: Badge
|
||||
`rgb(180, 83, 9)`, Warnung „Zeile 1: …", Tooltip und `aria-label` benennen
|
||||
den Grund; im exportierten SVG das Konflikt-Badge bernstein und das implizite
|
||||
M weiß mit Petrol-Rand. Kanonisches Beispiel §10 und der mitgelieferte Plan:
|
||||
0 Warnungen.
|
||||
|
||||
@@ -325,6 +325,38 @@ effektiv nicht weiter sein als das, was er braucht.
|
||||
- Anzeige: petrolfarbenes Badge (`--or`, `#0F766E`) mit weißer Schrift oben
|
||||
rechts an der Knoten-Ecke.
|
||||
|
||||
**Größen-Konflikt** — die angegebene Größe eines Elements muss zu seinen
|
||||
Teilpaketen passen. Die Skala ist ordinal; für diese eine Prüfung wird jede
|
||||
Größe als **Bereich** gelesen (Untergrenzen verdoppeln sich):
|
||||
|
||||
| | XS | S | M | L | XL | XXL |
|
||||
|---|---|---|---|---|---|---|
|
||||
| Bereich | [1, 2) | [2, 4) | [4, 8) | [8, 16) | [16, 32) | [32, ∞) |
|
||||
|
||||
- **Konflikt** ist, wenn selbst die **günstigste Lesart der Kinder** die
|
||||
**großzügigste Lesart des Elternknotens** erreicht: Summe der Untergrenzen
|
||||
der zählenden Kinder ≥ Obergrenze der Elterngröße. Gemeldet wird also nur,
|
||||
was unter *jeder* Lesart falsch ist — `(XL) = XL-Kind + 2 × M` (16+4+4 = 24
|
||||
< 32) ist deshalb kein Konflikt, vier `(S)` unter einem `(M)` (8 ≥ 8) schon.
|
||||
Ein Kind, das **größer** ist als sein Elternknoten, ist immer ein Konflikt
|
||||
(Teilmenge der Regel).
|
||||
- **XXL hat keine Obergrenze** — ein XXL-Element warnt nie: Für die großen
|
||||
Sammelknoten eines Plans behauptet die Skala schlicht keine Schranke mehr.
|
||||
- **Es zählen nur die direkten Kinder**, und davon nur die mit **angegebener**
|
||||
Größe (fehlende Größe ist keine Aussage; anders als beim günstigsten Pfad
|
||||
wird hier kein `M` angenommen). Verworfene und optionale (`+`) Kinder zählen
|
||||
nicht. In einer disjunktiven Gruppe (`|`/`=`) wird nur eine Alternative
|
||||
realisiert — dort zählt die **kleinste**.
|
||||
- Ein Elternknoten **ohne** Größe macht keine Aussage und wird nie geprüft;
|
||||
die Gegenrichtung (Eltern größer als die Kindersumme) warnt nicht — sie
|
||||
heißt nur, dass die Zerlegung unvollständig ist (dafür gibt es den
|
||||
Geister-Knoten).
|
||||
- **Meldung und Anzeige:** Warnung `sizeConflict` mit der Zeilennummer des
|
||||
Elternknotens; sein Größen-Badge wechselt auf die Warnfarbe (`--warn`,
|
||||
`#B45309`, weiße Schrift), Tooltip und `aria-label` benennen den Grund.
|
||||
Badge-Färbung auch im Grafikexport und im Druck. **Nichts wird automatisch
|
||||
korrigiert** — die Größen bleiben, wie sie geschrieben sind. Siehe D62.
|
||||
|
||||
## 6. Links
|
||||
|
||||
- Ein nacktes `https://…`-Token macht den ganzen Knoten klickbar
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
%% Every node carries an id; the block behind --- explains it. Hover a node.
|
||||
|
||||
[~] #wb: Werkbaum (XXL) https://github.com/mhoennig/werkbaum
|
||||
- [^] #not: Notation (L)
|
||||
- [^] #not: Notation (XL)
|
||||
- [^] #not.line: Line format (M)
|
||||
- [^] #not.status: Status box, eight states (S)
|
||||
- [^] #not.size: T-shirt size and the "decompose from M" rule (S)
|
||||
@@ -23,6 +23,7 @@
|
||||
- [^] #not.desc: Node descriptions, shown as a tooltip (M)
|
||||
- [^] #not.desc.short: A short text right at the node (S) %% a " line below the node
|
||||
- [^] #not.desc.long: A long block at the end, addressed by its id (S) %% behind a --- separator
|
||||
- [x] #not.sizes: Size conflict — sub-packages must fit the given size (S) %% ranges, XXL open-ended, see D62
|
||||
- [-] #not.store: A separate storage format for the structure (L) %% the text is the format
|
||||
- [ ] #not.ticket: Ticket references (#123,#US-123) (S) %% Taiga writes #US-123 for a user story
|
||||
- [^] #not.llms: llms.md, the notation explained to AI agents (S)
|
||||
@@ -30,8 +31,8 @@
|
||||
- [?] #not.benefit: Benefit per node, not only cost (M)
|
||||
| [?] #not.benefit.sigil: Another sigil next to the size (S)
|
||||
| [?] #not.benefit.points: Story points behind the T-shirt sizes (M)
|
||||
- [~] #ed: Editor (XL) https://werkbaum.javagil.de
|
||||
- [^] #ed.live: Live diagram from text (M)
|
||||
- [~] #ed: Editor (XXL) https://werkbaum.javagil.de
|
||||
- [^] #ed.live: Live diagram from text (L)
|
||||
- [^] #ed.parser: Parser (S)
|
||||
- [^] #ed.render: Renderer, three layout modes (M)
|
||||
- [^] #ed.render.horiz: Horizontal fan (S)
|
||||
@@ -40,7 +41,7 @@
|
||||
- [^] #ed.deplink: Cross links for dependencies (M)
|
||||
- [^] #ed.deplink.calm: Visually secondary — the tree carries the message (S)
|
||||
- [^] #ed.deplink.focus: Highlight what a selected node needs and feeds (S)
|
||||
- [^] #ed.path: Cheapest path (M)
|
||||
- [^] #ed.path: Cheapest path (L)
|
||||
- [^] #ed.path.cost: Cost model, missing size counts as M (S)
|
||||
- [^] #ed.path.front: Only the open front: what is done costs nothing (S) %% see D46
|
||||
- [^] #ed.path.started: Started work is on the path, extras included (S) %% see D61
|
||||
@@ -73,7 +74,7 @@
|
||||
- [^] #ed.shortid: Type #.kc under #prod-stage, get #prod-stage.kc (XS) %% an input aid, not notation
|
||||
- [^] #ed.showids: A # button shows the ids before the titles (XS) %% written as in the text, with a colon
|
||||
- [^] #ed.lineno.tip: The warning is the tooltip of its number (XS) %% no hunting in the list
|
||||
- [^] #ed.fold: Collapsible subtrees (M)
|
||||
- [^] #ed.fold: Collapsible subtrees (L)
|
||||
- [^] #ed.fold.marks: Initial state from the text: > collapses, < re-opens (S)
|
||||
- [^] #ed.fold.click: Fold and unfold in the diagram (S)
|
||||
- [^] #ed.fold.write: Folding writes the mark back into the text (S) %% one undo step
|
||||
@@ -87,7 +88,7 @@
|
||||
- [?] #ed.dates: Dates and milestones (M)
|
||||
| [?] #ed.dates.attr: An attribute in the line (S)
|
||||
| [?] #ed.dates.view: A separate timeline view (L)
|
||||
- [^] #bld: Build and delivery (M)
|
||||
- [^] #bld: Build and delivery (L)
|
||||
- [^] #bld.bundle: One self-contained file, bundled by Vite (S)
|
||||
- [^] #bld.tests: Unit tests (S)
|
||||
- [^] #bld.pages: Published on every push (S)
|
||||
@@ -110,9 +111,9 @@
|
||||
- [?] #be.auth: Accounts and permissions (L) :#be.docs
|
||||
| [?] #be.auth.token: Single user, one token (S)
|
||||
| [?] #be.auth.oidc: Log in with OIDC (L)
|
||||
- [~] #col: Collaborating on a Diagram (XL)
|
||||
- [~] #col: Collaborating on a Diagram (XXL)
|
||||
" The pad merges concurrent edits, Werkbaum renders the plan.
|
||||
- [^] #col.pad: Watch a shared Etherpad — it merges, we render (S)
|
||||
- [^] #col.pad: Watch a shared Etherpad — it merges, we render (L)
|
||||
- [^] #col.pad.export: Load the pad's plain-text export (XS)
|
||||
- [^] #col.pad.reload: Reload on demand (XS) %% a timer only trips Etherpad's rate limit
|
||||
- [^] #col.pad.embed: Embed the pad, keep the text mirror beside it (M)
|
||||
@@ -123,7 +124,7 @@
|
||||
| [ ] #col.pad.owntab: Live with it: edit in the pad's own tab (XS)
|
||||
- [^] #col.pad.point: A shared pointer: !!! focuses a node for everyone (XS)
|
||||
- [?] > #col.auto: Update by itself, within about 2 s (M)
|
||||
| [?] #col.own: Our own Etherpad beside Werkbaum (M) %% then every setting below is ours
|
||||
| [?] #col.own: Our own Etherpad beside Werkbaum (L) %% then every setting below is ours
|
||||
- [ ] #col.own.db: Node and a real database in userland (S) %% the flatfile store is not for production
|
||||
- [ ] #col.own.ram: Book the RAM quota for an own daemon (XS)
|
||||
- [ ] #col.own.proxy: Reverse proxy in .htaccess, websockets included (S) %% Mattermost does it, wiki shows how
|
||||
@@ -295,6 +296,12 @@
|
||||
are its text. There is no closing fence, so there is none to forget — this
|
||||
section is its own example.
|
||||
|
||||
#not.sizes
|
||||
Every size is read as a range whose lower bounds double, XXL open-ended. A
|
||||
warning appears only when even the most optimistic reading of the children
|
||||
exceeds the most generous reading of the parent — nothing is corrected
|
||||
automatically, the badge just turns amber.
|
||||
|
||||
#not.store
|
||||
Rejected on purpose: the text is the format. A second representation of the
|
||||
structure would need syncing, and the first disagreement between the two
|
||||
|
||||
Reference in New Issue
Block a user