notation: + für optionale Knoten — Zugaben statt Pflicht oder Alternative

Die Notation kannte nur „erforderlich" (-) und „wählbar" (|). Ein einzelnes
zusätzliches Feature — weder nötig noch Alternative zu etwas anderem — musste
als normales -Kind notiert werden und log damit. Feature-Modelle (FODA)
unterscheiden seit den 90ern mandatory/optional/alternative; `+` ergänzt die
fehlende zweite Beziehung. Mnemonik: `-` Teilpaket, `+` Zugabe, `|` Alternative.

Anlass ist nicht die Optik, sondern der günstigste Pfad (D18): markCheapest()
lief bei all-of über ALLE Kinder, jede Zugabe steckte also im errechneten
Minimum — systematisch zu groß, und zwar umso mehr, je ehrlicher ein Plan auch
die Kür notiert. Sichtbar wird es beim Alternativenvergleich: eine Alternative
mit teurer Zugabe verlor gegen eine schlichtere, obwohl die Zugabe gar nicht
dazugehört.

- Parser setzt `optional:true` und lässt `type:'and'` stehen — `+` gehört zum
  Knoten, nicht zur Gruppe. Dadurch bleiben gateOf() und die mixedGate-Warnung
  unverändert richtig: sie meldet weiter genau dann, wenn | mit -/+ gemischt
  wird. `-` neben `+` ist erlaubt und still — „diese drei sind nötig, das hier
  wäre schön" ist der Normalfall, nicht der Fehlerfall.
- Aus dem Pfad fallen optionale Knoten über pathChildren() heraus, die eine
  Stelle, die cheapestCost() und markCheapest() gemeinsam nutzen — deshalb
  wirkt es samt Teilbaum.
- Darstellung: hohler Kreis mittig auf der Knotenkante, wo der Abzweig
  auftrifft (FODA-Konvention). Bewusst KEIN dritter Linienstil: im kompakten
  Modus codiert allein der Stil das Gate (D15), gepunktet müsste sich dort
  gegen gestrichelt-grau behaupten. Der Kreis ist orthogonal dazu.
  CSS-Grundfall ist gestapelt (links/50 %), Ausnahme der horizontale Fächer
  (oben/50 %), Rück-Ausnahme der gestapelte all-of-Teilbaum unter any-of (D18)
  — andersherum wären es vier Ausnahmen statt zwei.
- SVG-Export zeichnet den Kreis NACH den Knoten (optMarks, Schritt 3a): er
  liegt halb außerhalb der Box und würde sonst vom Knoten-Rechteck überdeckt.
- Legende, Knoten-Tooltip und aria-label in allen neun Sprachen; hint_root
  formuliert die neue Mischregel.

Bekannte Schwäche, bewusst in Kauf genommen: Bei aktivem Pfad-Umschalter wird
der optionale Knoten ausgeblasst (opacity:.32) — und mit ihm sein Kreis, der
die Erklärung dafür wäre. `opacity` am Elternteil schlägt auf Pseudoelemente
durch, das lässt sich nicht zurücknehmen. Das Zurücktreten ist hier die
Hauptaussage (wie bei nicht gewählten Alternativen), Tooltip/aria/Legende
liefern die Begründung nach.

Verhaltensänderung: `+` am Zeilenanfang ist jetzt ein Zeichen und nicht mehr
Teil des Labels (`+ 5 % Puffer` ergibt „5 % Puffer"). Test-abgedeckt.

SPEC §1/§3/§9/§10 zuerst, dann Code (CLAUDE). Das kanonische Beispiel in §10
enthält jetzt eine `+`-Zeile und ist mit der Test-Fixture wieder deckungsgleich.
Der mitgelieferte Werkbaum-Plan markiert Drucklayout, „Was ist neu?" und die
Personenfarben als Zugaben — „Was ist neu?" war der Auslöser der Frage.

Verifiziert: 12 neue Tests (Parser setzt optional/type; Status/Größe/Tags/URL
am +-Knoten; führendes + wird verbraucht; Pfad lässt Zugabe samt Teilbaum aus;
Kosten des Elternknotens ohne Zugabe; Alternativenvergleich ohne Zugaben;
optionale Knoten bleiben sichtbar; opt-Klasse; keine Warnung bei -/+; Warnung
bei |/+; aria-label). Vitest 58/58, Snapshot zeigt `node opt` OHNE `cheap`.
Im Browser in allen drei Modi angesehen: Kreis sitzt in horizontal oben mittig,
in vertikal und kompakt links auf halber Höhe, jeweils genau auf dem Ende des
Abzweigs; SVG-Export enthält beide Kreise an denselben Punkten (gerendert
geprüft, nicht nur im Quelltext).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
mhoennig
2026-07-27 20:58:52 +02:00
co-authored by Claude Opus 4.8
parent b7637d6393
commit aa3e087ee6
13 changed files with 336 additions and 53 deletions
+24
View File
@@ -631,6 +631,30 @@
font-weight:400;
}
/* --- OPTIONAL (`+`, SPEC §3/D29): hohler Kreis am Abzweig ---
Kein dritter Linienstil: gestrichelt gehört any-of, und im kompakten Modus
codiert allein der Stil das Gate (D15). Stattdessen die FODA-Konvention —
gefüllter Punkt = erforderlich (hier: gar keiner), hohler Punkt = optional.
Der Kreis sitzt MITTIG AUF der Knotenkante, wo der Abzweig auftrifft.
Grundfall ist die gestapelte Anordnung (vertikal, kompakt, unter any-of):
Abzweig kommt von links auf halber Knotenhöhe. */
.node.opt::before{
content:'';position:absolute;
left:-5px;top:50%;transform:translateY(-50%);
width:10px;height:10px;border-radius:50%;
background:var(--card);border:2px solid var(--line);
box-sizing:border-box;
}
/* Horizontaler Fächer: der Abzweig kommt von oben mittig. */
.tree:not(.vertical):not(.kompakt) ul.and>li>.node.opt::before{
left:50%;top:-5px;transform:translateX(-50%);
}
/* …ausgenommen der gestapelte all-of-Teilbaum unter einer any-of-Gruppe
(siehe oben): dort kommt der Abzweig wieder von links. */
.tree:not(.vertical):not(.kompakt) ul.or>li.has-and>ul.and>li>.node.opt::before{
left:-5px;top:50%;transform:translateY(-50%);
}
/* ---------- Status (Pastell) ---------- */
.node.st-idee {background:#EBEDEF;border-color:#A2ABB5;color:var(--ink)}
.node.st-geplant {background:#EBE4F6;border-color:#A991D4;color:var(--ink)}