From 3ba37fc5a0d59c44a914da8a174a2c949e39c1ef Mon Sep 17 00:00:00 2001 From: mhoennig Date: Sat, 22 Aug 2026 16:25:09 +0200 Subject: [PATCH] =?UTF-8?q?feat:=20Abh=C3=A4ngigkeiten=20(`:#a,#b`)=20pars?= =?UTF-8?q?en=20=E2=80=94=20unbekannte=20ID=20warnt,=20Zyklen=20zul=C3=A4s?= =?UTF-8?q?sig=20(SPEC=20=C2=A71,=20D37)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Extraktionsschritt 7: alle alleinstehend angesetzten `:#a,#b`-Token — EIN zusammenhängendes Token ohne Leerraum, mehrere je Zeile werden zusammengeführt; eingeklammerte Erwähnungen wie `(:#auth,#api)` bleiben Label (Zitier-Konvention wie `(#auth)`, deshalb enger als die frühere §11-Formulierung). `deps` sind ID-Strings am Knoten; der Parser prüft nur Existenz (Warnung unknownDep, Vorwärts-Referenzen normal), Zyklen werden bewusst nicht einmal erkannt. Sichtbar als `→ #a, #b` im Tooltip und als a11yDeps; Legendenzeile + Warntext in allen 9 Sprachen. Im mitgelieferten Plan: Zyklen-legal und unknown-id-Warnung auf [x], Abhängigkeits-Knoten auf [~]. 12 neue Tests (tests/deps.test.js). Co-Authored-By: Claude Fable 5 --- docs/DECISIONS.md | 38 +++++++++++++++ docs/SPEC.md | 51 +++++++++++++------- docs/TASKS.md | 7 ++- docs/examples/werkbaum.werkbaum | 6 +-- frontend/CLAUDE.md | 10 +++- frontend/index.html | 1 + frontend/src/app.js | 37 +++++++++++---- frontend/src/parser.js | 29 ++++++++++-- frontend/src/render.js | 7 ++- frontend/src/warnings.js | 5 ++ frontend/tests/deps.test.js | 82 +++++++++++++++++++++++++++++++++ frontend/tests/ids.test.js | 5 +- 12 files changed, 239 insertions(+), 39 deletions(-) create mode 100644 frontend/tests/deps.test.js diff --git a/docs/DECISIONS.md b/docs/DECISIONS.md index 9b72d0f..32a4b34 100644 --- a/docs/DECISIONS.md +++ b/docs/DECISIONS.md @@ -1702,3 +1702,41 @@ welcher Knoten bei Verweisen „gewinnt“, entscheidet erst die Abhängigkeits-Auflösung — dort ist die Warnung dann schon da. Eine Zeile, die **nur** aus einer ID besteht, wird wie jede leere Zeile ignoriert und belegt die ID nicht. + +## D37 — Abhängigkeiten (`:#a,#b`) geparst: ein Token, alleinstehend, IDs als Strings +Der zweite Baustein der Phase-4-Kette. Gebaut ist die **Schreibweise** (SPEC +§1); die Konsumenten — effektiver Status, Querverbindungen, Closure-Rechnung — +bleiben eigene Schritte (§11). Vier Festlegungen: + +**Die Liste ist ein zusammenhängendes Token, ohne Leerraum.** `:#a,#b` — jede +ID mit `#`, kommagetrennt, kein Leerzeichen. Das ist die wörtliche Lesart von +§11 („Doppelpunkt mit unmittelbar folgendem `#`“) und macht das Zeilenformat +robust: Wo das Token endet, beginnt wieder gewöhnlicher Text. Der bekannte +Stolperstein steht in der SPEC: `:#a, #b` liest nur `#a`, und das ` #b` +dahinter ist ein alleinstehendes Token — also die Knoten-ID. Ein Rest wie ein +verwaistes `,b` bleibt sichtbar im Label stehen und verrät den Tippfehler, +statt verschluckt zu werden. + +**Nur alleinstehend angesetzt** (`(^|\s):#…`) — **enger als §11**, das nur den +folgenden `#` verlangte. Der Ausschlag: die **Zitier-Konvention**. Bei den IDs +(D36) wurden Erwähnungen im mitgelieferten Plan eingeklammert (`(#auth)`), und +genau das muss auch für Abhängigkeiten funktionieren — `(:#auth,#api)` als +Erwähnung im Label wäre mit der reinen `:#`-Regel trotzdem geparst worden +(die Klammer steht ja vor dem `:`, nicht vor dem `#`). Mit der +Alleinstehend-Regel schützen Klammern einheitlich alles: `(#auth)`, +`(:#a,#b)`, künftig `(&tag)`. Nebeneffekt: Konstrukte wie `1:#2` im Label +bleiben Label. + +**`deps` sind ID-Strings, keine Knoten-Referenzen.** Der Parser prüft nur +**Existenz** (`unknownDep` je fehlender ID, an der Zeile des abhängigen +Knotens); aufgelöst wird erst beim Konsumenten. So bleibt der Parse-Baum +serialisierbar und die Auflösungsfragen (doppelte IDs — die `duplicateId`- +Warnung steht dann schon da) liegen dort, wo sie beantwortet werden können. +**Zyklen werden bewusst nicht einmal erkannt**: Sie sind zulässig (§1/D34, +„wird gemeinsam fertig“), eine Zyklenprüfung hätte also keinen Abnehmer — +auch die Selbst-Abhängigkeit ist stumm. + +**Sichtbar als `→ #a, #b` im Tooltip und als `a11yDeps` im `aria-label`** — +dieselbe Zurückhaltung wie bei den IDs (D36): keine eigene Diagramm- +Darstellung, bis die Querverbindungen (§11) gebaut sind; der Pfeil im Tooltip +sagt die Richtung („hängt ab von“), ohne ein neues Zeichen einzuführen. diff --git a/docs/SPEC.md b/docs/SPEC.md index 4603227..5f1e97a 100644 --- a/docs/SPEC.md +++ b/docs/SPEC.md @@ -20,8 +20,9 @@ dieser Reihenfolge (wichtig für Kollisionsfreiheit): 4. Größe: erstes `(XS|S|M|L|XL|XXL)`, Groß-/Kleinschreibung egal. 5. Tags: alle `@name`-Vorkommen. 6. Knoten-ID: das **erste** alleinstehend angesetzte `#name`-Token (siehe unten). -7. Fokusmarke: `!!!` als **alleinstehendes** Token (siehe unten). -8. Rest, whitespace-normalisiert = Label. Leeres Label ⇒ Zeile ignorieren. +7. Abhängigkeiten: alle alleinstehend angesetzten `:#a,#b`-Token (siehe unten). +8. Fokusmarke: `!!!` als **alleinstehendes** Token (siehe unten). +9. Rest, whitespace-normalisiert = Label. Leeres Label ⇒ Zeile ignorieren. **Knoten-ID `#name`** — benennt einen Knoten im **ganzen Dokument** eindeutig; sie ist die Adresse für Abhängigkeiten und Beschreibungsblöcke (§11). @@ -41,6 +42,27 @@ sie ist die Adresse für Abhängigkeiten und Beschreibungsblöcke (§11). ID gilt trotzdem am Knoten (fehlertolerant wie §4 — die Zeile geht nicht verloren). +**Abhängigkeiten `:#a,#b`** — der Knoten hängt von den Knoten mit diesen IDs +ab, auch außerhalb seines eigenen Teilbaums. + +- Die Liste ist **ein zusammenhängendes Token**: Doppelpunkt, dann + kommagetrennt je ID mit `#`, **ohne Leerraum**. `:#a, #b` liest nur `#a` — + das ` #b` dahinter ist ein alleinstehendes Token und damit die Knoten-ID + (siehe oben). Mehrere Listen je Zeile werden zusammengeführt. +- Erkannt nur **alleinstehend angesetzt** (`(^|\s):#…`, enger als die frühere + §11-Formulierung — Begründung: D37): Ein Doppelpunkt im Label bleibt Label, + und eine **eingeklammerte Erwähnung** wie `(:#auth,#api)` bleibt Label — + dieselbe Zitier-Konvention wie `(#auth)` bei der Knoten-ID. +- Abhängigkeiten sagen **nichts über Reihenfolge oder Startzeitpunkt** — sie + sagen etwas über den **Status** (effektiver Status, §11). Das trennt + Werkbaum von einem Netzplan. +- **Zyklen sind zulässig** und bedeuten: diese Knoten werden gemeinsam fertig. + Kein Fehler, keine Warnung — auch die Abhängigkeit auf sich selbst nicht. + Vorwärts-Referenzen (Ziel steht weiter unten) sind normal. +- Eine ID ohne zugehörigen Knoten: Warnung `unknownDep` mit Zeilennummer. +- Sichtbar im Tooltip (`→ #a, #b`) und im `aria-label`; die Querverbindungen + im Diagramm sind reserviert (§11). + **Fokusmarke `!!!`** — „schau hier hin": Der Knoten wird im Diagramm hervorgehoben und ins Bild geholt (§9). Gedacht für das gemeinsame Arbeiten an einem Pad (§9, `?etherpad=`): Weil dort niemand den Cursor der anderen sieht, @@ -70,7 +92,13 @@ Für die Knoten-ID (Schritt 6, nur der erste Treffer): (^|\s)#([\p{L}\p{N}._-]+) ``` -Für die Fokusmarke (Schritt 7): +Für die Abhängigkeiten (Schritt 7, alle Treffer): + +``` +(^|\s):#([\p{L}\p{N}._-]+(?:,#[\p{L}\p{N}._-]+)*) +``` + +Für die Fokusmarke (Schritt 8): ``` (^|\s)!!!(?=\s|$) @@ -582,19 +610,10 @@ unten; damit ist die frühere Dreifach-Rolle von `#` aufgelöst (D34). ### Abhängigkeiten zwischen Knoten (`:#auth,#api`) -Ein Knoten kann von Knoten **außerhalb seines eigenen Teilbaums** abhängen; -notiert als Doppelpunkt mit kommagetrennter ID-Liste. - -- Abhängigkeiten sagen **nichts über Reihenfolge oder Startzeitpunkt** — sie - sagen etwas über den **Status** (siehe unten). Das ist der Unterschied zu - einem Netzplan. -- **Zyklen sind zulässig** und bedeuten: diese Knoten werden gemeinsam fertig. - Sie sind also kein Fehler und bekommen keine Warnung. -- Eine ID ohne zugehörigen Knoten ist ein Fehler (Warnung mit Zeilennummer). -- Kollisionsfrei zur bestehenden Zeilenextraktion (§1): Gelesen wird nur ein - Doppelpunkt mit **unmittelbar folgendem `#`** — ein Doppelpunkt im Label - bleibt damit Label. Die URL wird ohnehin schon in Schritt 3 herausgenommen, - vor Größe, Tags und Label. +Die **Schreibweise ist umgesetzt** — Definition jetzt in §1 (Token-Vertrag, +Alleinstehend-Regel, Zyklen zulässig, Warnung `unknownDep`). Offen sind die +**Konsumenten**: der effektive Status, die Querverbindungen im Diagramm und +die Closure-Rechnung des günstigsten Pfads (alle drei unten). ### Intrinsischer und effektiver Status diff --git a/docs/TASKS.md b/docs/TASKS.md index 8f60755..f9862da 100644 --- a/docs/TASKS.md +++ b/docs/TASKS.md @@ -77,7 +77,12 @@ entscheiden, **bevor** Code entsteht. unberührt); Warnung `duplicateId` nennt beide Zeilen; sichtbar im Tooltip + `aria-label`; SPEC-§11-Teil nach §1 überführt; `tests/ids.test.js`. -- [ ] Abhängigkeiten `:#a,#b` parsen; unbekannte ID → Warnung, Zyklen erlaubt. +- [x] Abhängigkeiten `:#a,#b` parsen; unbekannte ID → Warnung, Zyklen erlaubt. + → Umgesetzt (D37): ein zusammenhängendes Token ohne Leerraum, nur + alleinstehend angesetzt (eingeklammerte Erwähnungen bleiben Label); + `deps` als ID-Strings am Knoten, Warnung `unknownDep`, Zyklen bewusst + ungeprüft; sichtbar als `→ #a, #b` im Tooltip + `a11yDeps`; + SPEC-§11-Teil nach §1 überführt; `tests/deps.test.js`. - [ ] Effektiven Status rechnen (intrinsisch + Abhängigkeiten); Darstellung entscheiden — die Knotenfarbe zeigt heute den intrinsischen Status. - [ ] Günstigsten Pfad auf die Dependency Closure umstellen (gemeinsame diff --git a/docs/examples/werkbaum.werkbaum b/docs/examples/werkbaum.werkbaum index ea643ce..c58490f 100644 --- a/docs/examples/werkbaum.werkbaum +++ b/docs/examples/werkbaum.werkbaum @@ -12,11 +12,11 @@ - [x] Node IDs (#auth) (S) %% often just the ticket number, see SPEC §11 + [?] Free tags, &tag (M) %% only together with a consumer, see D34 - [?] A lens: highlight every node tagged &x (S) - - [?] Dependencies across the tree, :#auth,#api (M) + - [~] Dependencies across the tree, (:#auth,#api) (M) - [?] They constrain the status, not when work may start (S) - [?] Effective status: intrinsic, plus what it depends on (S) - - [?] Cycles are legal — they mean "finished together" (XS) - - [ ] An id with no node behind it is a warning (XS) + - [x] Cycles are legal — they mean "finished together" (XS) + - [x] An id with no node behind it is a warning (XS) - [x] XOR, = — exactly one alternative, not at least one (S) - [?] Node descriptions, shown as a tooltip or pop-up (M) - [?] A short text right at the node (S) %% indentation already means hierarchy diff --git a/frontend/CLAUDE.md b/frontend/CLAUDE.md index c078e39..93b7644 100644 --- a/frontend/CLAUDE.md +++ b/frontend/CLAUDE.md @@ -47,8 +47,8 @@ verworfene Elemente. Quelle sind ES-Module unter `src/`; `index.html` ist der Alternative-Rahmen grau (Basis-CSS `ul.or`). Kein Petrol im Diagramm mehr; `var(--or)` nur noch für UI-Akzente/Logo (SPEC §9, D15). - Extraktionsreihenfolge im Parser nicht umstellen: Kommentar → Zeichen/ - Status → URL → Größe → Tags → Knoten-ID → Fokusmarke (sonst kollidieren - `@` und `#` in URLs). + Status → URL → Größe → Tags → Knoten-ID → Abhängigkeiten → Fokusmarke + (sonst kollidieren `@` und `#` in URLs). - Fehlertoleranz (SPEC §4): der Parser erfasst die Statusbox als *beliebiges* Einzelzeichen `\[([^\]])\]` und validiert gegen `STATUS_BY_CODE`; unbekannte Codes → `parse().warnings` als `{type:'unknownStatus', line, code}`, Knoten @@ -273,6 +273,12 @@ verworfene Elemente. Quelle sind ES-Module unter `src/`; `index.html` ist der darf nicht gefressen werden. Zeichenmenge wie `@name`. Doppelte ID → `{type:'duplicateId', line, id, firstLine}`; die spätere gilt trotzdem. Keine eigene Darstellung — nur Tooltip (erste Position) und `a11yId`. +- Abhängigkeiten `:#a,#b` (SPEC §1/D37): EIN zusammenhängendes Token ohne + Leerraum, nur **alleinstehend angesetzt** — `(:#a,#b)` bleibt Label + (Zitier-Konvention wie `(#auth)`). `node.deps` sind **ID-Strings**, keine + Knoten-Referenzen; der Parser prüft nur Existenz (`unknownDep`), Zyklen + werden bewusst nicht einmal erkannt (zulässig, „gemeinsam fertig"). Keine + Diagramm-Darstellung — nur Tooltip (`→ #a, #b`) und `a11yDeps`. - XOR-Gruppen `=` (SPEC §3/D35): Der Parser setzt `type:'xor'` (nur mit folgendem Leerraum — `=SUMME(…)` bleibt Label); der Renderer gibt `