Prüfbericht · Diagramm-Engine

Sieben Tickets in Review, eines davon im Detail

MZP-55 war der Auftrag. Die Quittung besteht ihre eigene Abnahme — und bricht dabei ein Versprechen, das im Werkzeugtext steht.

Datum
2026-09-03
Prüfling
publisher.maierzone.de, ausgerollter Stand
Umfang
MZP-55 im Detail, dazu alle übrigen Tickets in REVIEW: MZP-40, 52, 53, 54, 57, 58
Mittel
Live-Fixtures über publishDiagram, diffDiagram, validateDiagram; echter Chromium über CDP für den Klick-Durchlauf; curl gegen die ausgelieferten Bundles
Nebenprodukt
Die Arbeit Kammzahn — der Prüfstand ist zugleich das Werkstück

Verdikt

Vier Tickets sind abnahmefähig, zwei brauchen eine Nachbesserung, eines lässt sich von außen gar nicht abnehmen. Die beiden Bugs MZP-57 und MZP-58 sind still behoben worden — sie lassen sich nicht mehr nachstellen.

Abnahmefähig
4von 7
Nachbessern
2Tickets
Diagramm-Bundles geprüft
23live
Konsolenfehler im Viewer
0in 9 Läufen
TicketGegenstandVerdiktIn einem Satz
MZP-55 Diagramm-Delta mit Quittung nachbessern Alle drei Abnahmekriterien wörtlich erfüllt, aber ein verschobener Knoten erzeugt zwei rerouted-Zeilen — die Werkzeugbeschreibung sagt, er erzeuge keinen Eintrag.
MZP-52 Kanten- und Knoten-Semantik im SVG abnahmefähig Alle fünf Kanten- und alle vier Knoten-Attribute live bestätigt; ein Kriterium bleibt mangels Referenz-Slugs unprüfbar, ohne dass das die Umsetzung belastet.
MZP-53 Interaktiver Viewer abnahmefähig Alle sieben Funktionen im echten Browser durchgefahren — Suche, Fokus, Reach, Route, Legende, Deep-Link, Theme — ohne einen Konsolenfehler.
MZP-54 Geführte Kapitel, Präsentationsmodus nachbessern Der offene Klick-Durchlauf ist jetzt grün; prefers-reduced-motion wird weiterhin ignoriert, und das steht wörtlich in der Abnahme.
MZP-57 Zone ohne Mitglieder abnahmefähig Nicht mehr nachstellbar: das Geisterrechteck bleibt weg, und der Validator warnt jetzt feldgenau statt zu schweigen.
MZP-58 Doppelte Edge-Ids abnahmefähig DUPLICATE_EDGE_ID ist da, als Error, mit beiden betroffenen Kanten im Klartext.
MZP-40 Wiki-Editor, private Seiten nicht abnehmbar Die priv--Sperre greift; der Editor liegt hinter /admin und ist von außen weder sicht- noch prüfbar.

Die Arbeit: Kammzahn

Ein Prüfbericht über einen Diagramm-Viewer, der selbst kein Diagramm zeigt, prüft nichts. Also ist der Prüfstand das Werkstück: Kammzahn nimmt das Thema Quantennarben aus dem Vault und stellt den Apparat, mit dem Heller 1984 die Narbe sichtbar machte, als begehbares Diagramm.

Neun Knoten, neun Kanten, vier Zonen, ein Callout und fünf geführte Kapitel. Palette ist eine Cyanotypie — Preußischblau auf Papier, Rostrot für den Befund. Die Wahl ist nicht dekorativ: die Arbeit Narben hatte in ihrer Cyanotypie-Fassung die stärkere Wirkung, und ein Diagramm über ein fotografisches Messverfahren darf wie ein Lichtpausblatt aussehen.

Das Ding ist unten eingebettet, im Modus ?embed=1 — dem Modus, den MZP-54 verspricht. Der Bericht führt vor, was er beurteilt. Klicken Sie sich durch die Kapitel.

Abb. 1: Kammzahn, live eingebettet. Die Kapitelleiste unten fährt die Kamera; alles andere blendet ?embed=1 aus.

Was die Arbeit nebenbei geprüft hat: Auto-Layout und Handplatzierung, abgeleitete Zonen-Hüllen, eine Rückkopplungskante, die den Graphen zyklisch macht, ein Annotation-Callout, die eigene Palette über variant: custom — und, weil der Graph einen Zyklus hat, ob der Reach-Trace terminiert. Er terminiert.

MZP-55 im Detail

Das Ticket nennt drei Abnahmekriterien. Ich habe eine Fixture unter mzp-55-delta-fixture in vier Versionen publiziert, die genau diese drei Fälle trennt, und die Quittungen abgeholt.

VergleichWas sich änderteErwartung laut TicketQuittung
1 → 1 nichts leere Quittung changes: [], beide Hashes gleich erfüllt
1 → 2 Knoten d verschoben, sonst nichts kein changed kein changed — aber zwei rerouted: bc und cd wörtlich erfüllt
2 → 3 Kante bc umgeroutet (Waypoints, Andockseiten) rerouted, nicht changed zwei rerouted: bc und cd erfüllt
3 → 4 Label geändert, Knoten dazu, Kante weg, Kante dazu added / removed / changed genau das, mit Feld-Detail label: Alpha → Alpha prime erfüllt

Der Widerspruch

Die Zeilen 1 → 2 und 2 → 3 liefern dieselbe Quittung. Im einen Fall habe ich eine Box verschoben, im anderen eine Kante umgeroutet. Die Klasse rerouted, die das Ticket ausdrücklich als eigene Klasse einführt, weil sie fachliche Änderung von bloßer Führung trennt, trennt hier nichts mehr.

Und die Werkzeugbeschreibung von diffDiagram sagt wörtlich:

Aus der Tool-Beschreibung
„Verglichen wird ein layoutfreies kanonisches Modell — ein verschobener Knoten oder ein um 1px versetzter Kantenverlauf erzeugt KEINEN Eintrag.“

Ein verschobener Knoten erzeugt zwei Einträge. Besonders unangenehm: bc verbindet b und c — beide standen still. Die Meldung entsteht nur, weil der Router seine Andockpunkte neu verteilt hat.

Wo es herkommt — ein sauberes Gegenexperiment

Es gibt zwei Verdächtige: die Knotenkoordinaten selbst, oder die vom Server gerechnete Routen-Geometrie. Ein zweiter Slug trennt sie. mzp-55-delta-isoliert enthält einen Knoten z, an dem keine einzige Kante hängt. In Version 2 wird nur dieser Knoten um 200 px verschoben.

Quittung des Gegenexperiments
{
  "comparatorVersion": "1",
  "slug": "mzp-55-delta-isoliert",
  "fromVersion": 1, "toVersion": 2,
  "hashFrom": "sha256:d2982de23f6499b68ac8ba042d82cc65cf1a6d83e0ebf4708f5c7c673428ec9f",
  "hashTo":   "sha256:d2982de23f6499b68ac8ba042d82cc65cf1a6d83e0ebf4708f5c7c673428ec9f",
  "changes": []
}

Identische Hashes. Die Kanonisierung ist für Knoten wirklich layoutfrei — der erste Verdächtige ist entlastet. Übrig bleibt die Routen-Geometrie: der gerechnete Polygonzug einer Kante steckt im kanonischen Modell. Jede Bewegung im Bild lässt den Router neu rechnen, und jedes Neurechnen wird zu einer Zeile in der Quittung.

Das ist mehr als ein Schönheitsfehler, sobald Auto-Layout im Spiel ist. Dort verschiebt jeder neue Knoten alle anderen. Eine Quittung, aus der ein Agent laut Ticket „einen Änderungstext schreiben kann, ohne die Bilder anzusehen“, besteht dann überwiegend aus Rauschen, das kein Mensch verursacht hat.

Vorschlag
Die Route nicht als gerechneten Polygonzug kanonisieren, sondern als Absicht des Autors: props.fromSide, props.toSide und die ausdrücklich gelieferten waypoints. Dann wird 1 → 2 zur leeren Quittung, 2 → 3 bleibt rerouted — und die Klasse trägt wieder Information. Die Abnahmekriterien des Tickets bleiben davon unberührt; sie werden erst dadurch aussagekräftig.

Was an MZP-55 vorbildlich ist

Der fachliche Fall — Zeile 3 → 4 — ist tadellos. Feldgenaues Detail statt „hat sich geändert“, Hashes auf beiden Seiten, Comparator-Version in der Quittung. Und die Ansicht unter /delta ist genau die im Ticket beschriebene: drei Spalten, Zugänge grün, Abgänge rot durchgestrichen, Änderungen gelb, darunter die Tabelle aus der Quittung.

Auch die Fehlerfälle sitzen: eine unbekannte Version liefert DIAGRAM_DELTA_UNKNOWN_VERSION mit dem Hinweis auf listVersions, statt zu raten.

Abb. 2: Die Delta-Ansicht, live — der fachliche Fall 3 → 4, so wie das Ticket ihn beschreibt. Before, Delta, After nebeneinander, darunter die Quittung als Tabelle.

Die übrigen Tickets

MZP-52 · SVG-Semantik

An jeder Kante des Prüfstücks sitzen data-edge-key, -from, -to, -kind und, wo gezeichnet, -label; an jedem Knoten data-node-kind. Neun Kanten, neun eindeutige Schlüssel, keine Kollision. Der Schlüssel taucht doppelt auf — einmal an der Kantengruppe, einmal an der Labelgruppe — und das ist die dokumentierte Absicht aus dem Umsetzungskommentar, keine Dublette.

Offen bleibt das Pixel-Gleichheits-Kriterium: die fünf diagram-ref-*-Slugs sind auf der Instanz weiterhin 404. Das Kriterium ist so nicht prüfbar — es belastet die Umsetzung aber nicht, denn zusätzliche Attribute an einer <g> ohne transform ändern das Bild nachweislich nicht.

MZP-53 · Interaktiver Viewer

Alle sieben Funktionen an kammzahn im echten Browser durchgefahren. Suche findet den Knoten. Fokus meldet „4 Knoten direkt verbunden“ — richtig. Reach-Trace: 6 stromauf, 5 stromab, und weil die Rückkopplungskante den Graphen zyklisch macht, ist das der korrekte transitive Abschluss, kein Endlosläufer. Die Routen-Sonde liefert den vollständigen Pfad über sieben Kanten. Deep-Links setzen #focus=, #reach=, #route=. Legende zählt die Rollen. Null Konsolenfehler.

Gedimmt wird über Inline-opacity (0,28), nicht über die mitgelieferte Klasse .dv-dim — die ist toter Stil.

MZP-54 · Kapitel und Präsentation

Der offene Punkt ist geschlossen. Der Klick-Durchlauf, der laut Aktennotiz vom 1. September noch ausstand, ist gelaufen: fünf Kapitel, jeder Klick fährt die Kamera auf eine andere Hülle, aria-current wandert mit, die Notiz wechselt, der Deep-Link zählt #view=0…4, Pfeiltasten blättern zurück. ?present=1 und ?embed=1 lassen exakt Kapitelleiste und Diagramm stehen und blenden Suche, Legende, Route, Titel und Export aus.

Prüfpunkt 4 bleibt rot, und diesmal gemessen statt aus dem Quelltext geschlossen. Siehe Befund N1.

MZP-57 · Zone ohne Mitglieder

Nachgestellt, nicht reproduzierbar. Die leere Zone wird nicht mehr gezeichnet: kein data-zone-id, kein frei schwebendes Label, renderSummary.zones zählt 1 statt 2. Und statt zu schweigen sagt der Validator jetzt:

ZONE_WITHOUT_MEMBERS — „Zone ‚leer‘ hat keine Mitglieder … (Tippfehler in node.zone oder Zuordnung vergessen?)“

Das ist mehr, als das Ticket verlangt hat: Es hätte gereicht, das Rechteck wegzulassen. Der Hinweis auf den wahrscheinlichen Tippfehler erspart die Fehlersuche gleich mit.

MZP-58 · Doppelte Edge-Ids

Zwei Kanten mit "id": "dup" laufen nicht mehr durch. DUPLICATE_EDGE_ID, Severity ERROR, Subject edges[1], mit duplicateOf: edges[0] in den Details — und einer Meldung, die auch den Grund nennt: „sonst kollidiert der data-edge-key im SVG“.

Umgesetzt wurde der im Ticket bevorzugte Weg, nicht die Alternative: der Renderer uniquifiziert nicht still, der Validator lehnt ab. Richtig so — ein Spez-Fehler soll auffliegen.

MZP-40 · Editor und private Seiten

Zweite Sperre: greift. /published/priv-… liefert 404 — für existierende Seiten, für erfundene, für Großschreibung und für tiefe Pfade gleichermaßen. Keine Existenz wird verraten, und das ist genau die Wahl 404 statt 403 aus dem Ticket.

Editor: nicht abnehmbar. /admin antwortet pauschal mit 401 — auf erfundene Unterpfade genauso wie auf /admin/wiki. Von außen ist nicht einmal feststellbar, ob die Route existiert.

Zwei der drei Abnahmepunkte kann ich grundsätzlich nicht abhaken: die Bearbeitung im Browser und die Kante aus [[slug]] im Graphen. Der dritte, „404 auch am Reverse-Proxy vorbei“, verlangt laut Ticket ausdrücklich einen Test im Testlauf — von außen sind erste und zweite Sperre nicht unterscheidbar.

Nebenbefunde

Sechs Dinge, die neben den Tickets aufgefallen sind. Keines blockiert eine Abnahme.

  1. N1 Barrierefreiheit

    Die Kamerafahrt ignoriert prefers-reduced-motion. Gemessen, nicht gelesen: mit emulierter Einstellung meldet matchMedia korrekt true, und der Kapitelwechsel läuft trotzdem über zehn verschiedene viewBox-Zwischenwerte in rund 560 ms — deckungsgleich mit dem Verhalten ohne die Einstellung. Weder viewer.js noch viewer.css enthalten den String überhaupt.

    MZP-54 nennt das in der Abnahme wörtlich: „bei gesetztem prefers-reduced-motion bewegt sich nichts“. Ein Guard um animateCamera(), der auf den Zielwert springt, ist die ganze Kur.

  2. N2 sichtbar für Autoren

    meta.subtitle wird nirgends angezeigt. Jede Diagrammseite liefert die CSS-Regel .dd-subtitle mit — aber kein Element trägt die Klasse je. Der Untertitel überlebt nur als <desc> im SVG, also für Screenreader.

    Geprüft an fünf Seiten quer über interaktiv/nicht-interaktiv und handplatziert/auto-gelayoutet: überall Regel vorhanden, Element nirgends. Ein Autor, der einen Untertitel setzt, bekommt kommentarlos nichts.

  3. N3 Rauschen

    Jeder interaktive Publish meldet einen erfundenen fehlenden Verweis. Die Warnung lautet:

    MISSING_REFERENCE — Referenz ');\n var to = g.getAttribute(' zeigt auf eine Datei, die nicht im Bundle liegt

    Der Referenz-Scanner liest den Quelltext von viewer.js und hält ein Stück Stringverkettung für einen Dateipfad. Die Warnung kommt bei jedem validateDiagram und publishDiagram mit interactive: true — sie ist damit die erste Warnung, die Agenten routinemäßig zu ignorieren lernen, und das ist der teuerste Nebeneffekt.

  4. N4 Bestand

    Die MZP-52-Semantik gibt es nur auf Diagrammen, die nach dem Deploy publiziert wurden. Von 23 geprüften Diagramm-Bundles tragen 8 die Attribute, 15 nicht. Der Deploy rendert Bestandsseiten nicht neu — das ist bekannt und richtig, hat aber eine Folge: Viewer, /inspect und der Wissens-Graph aus MZP-38 finden auf zwei Dritteln des Bestands keinen Graphen.

    Ein Republish-Durchlauf über die Diagramm-Slugs wäre die naheliegende Kur. fluss-der-zeit-zyklus bleibt auch danach ohne Kanten-Attribute — es ist ein loop-Diagramm, und der LoopTypeRenderer ist in MZP-52 bewusst ausgenommen. Kein Fehler, aber eine Lücke, die der Graph kennen muss.

  5. N5 Kontrast

    Bei variant: custom nimmt die Kopfzeile ihre Farbe aus soft. In einer eigenen Palette ist soft typischerweise ein Flächen-Ton, kein Textton. Bei Kammzahn ergibt das für die Eyebrow-Zeile ein Kontrastverhältnis von 1,64 : 1 — die Zeile ist praktisch unsichtbar, während Knotentext (12,2 : 1) und Annotation (6,5 : 1) einwandfrei stehen.

    Die Kopfzeile sollte auf ink oder muted liegen, oder der Validator sollte den Kontrast der Chrome-Farben gegen paper prüfen, so wie er heute schon Geometrie prüft.

  6. N6 gut gelöst

    visualProof hat beim Bau dieser Arbeit echte Fehler gefangen. Der erste Entwurf lief über Auto-Layout; die Rückmeldung nannte sieben von neun Knoten mit Textüberstand von 4 bis 47 px, knotengenau. Ohne diese Zahl hätte ich das Bild für in Ordnung gehalten.

    Daraus zwei Wünsche, in dieser Reihenfolge: der Layouter sollte Knotenboxen an der Breite des sublabel bemessen, und Kleinigkeiten am Rand — die Delta-Seite schreibt „Zugaenge“ und „Aenderung“, während der Rest des Produkts „Schließen“ und „Aufwärts“ sauber setzt.

Was offen bleibt

Drei Dinge konnte ich nicht prüfen, und keines davon liegt an der Umsetzung:

  • Der Wiki-Editor aus MZP-40 liegt hinter einem pauschalen 401. Ob /admin/wiki existiert, ist von außen nicht feststellbar.
  • Die Pixel-Gleichheit aus MZP-52 braucht die fünf diagram-ref-*-Slugs. Sie sind auf der Instanz nicht publiziert (404) und waren es schon bei der Verifikation am 1. September nicht.
  • Ob die priv--Sperre aus der Anwendung oder aus nginx kommt, ist von außen nicht unterscheidbar — genau deshalb verlangt MZP-40 den Test im Testlauf.

Empfehlung

TicketEmpfohlener SchrittWarum
MZP-52nach DONEUmfang vollständig live bestätigt; das offene Kriterium hängt an fehlenden Fixtures, nicht an der Arbeit.
MZP-53nach DONEAlle sieben Funktionen im Browser durchgefahren, fehlerfrei. Der Klick-Durchlauf, den das Ticket dem Auftraggeber vorbehält, liegt jetzt als Protokoll vor.
MZP-57nach DONENicht mehr nachstellbar, Validator warnt über den Ticketumfang hinaus.
MZP-58nach DONEVorgeschlagener Weg umgesetzt, Meldung feldgenau.
MZP-54in REVIEW haltenNur noch wegen prefers-reduced-motion (N1). Ist der Guard drin, ist das Ticket fertig — Umfang 1 bis 3 sind es heute schon.
MZP-55in REVIEW haltenDie Kanonisierung der Routen-Geometrie nachziehen. Danach trägt rerouted wieder Information; alles andere am Ticket steht.
MZP-40Abnahme neu zuschneidenZwei der drei Punkte sind ohne Zugang nicht abnehmbar. Entweder ein Zugang für den Prüfer oder ein Test im Testlauf, der die zweite Sperre nachweist.