Meilensteine sind die nutzersichtbare Historie und bleiben; Sync-Versionen
tragen die Diffs des Live-Editings und werden nach der Aufbewahrungsfrist
verdichtet (D76). Ohne die Trennung wuerde die Historie beim getakteten
Schreiben zum Transaktionslog.
Die Schreibpause braucht keinen Zeitgeber: Die naechste Aenderung stellt
fest, dass eine Pause war, und befoerdert die Version davor nachtraeglich.
Strukturelle Aenderungen sind immer Meilensteine.
Das Historie-Repository greift jetzt gezielt zu (eine Version, juengster,
aeltester, Meilensteine, maxVersion) statt stets alle Eintraege zu laden und
in Kotlin zu filtern — bei hunderten Volltext-Versionen je Dokument war das
untragbar. Restore liest den letzten Stand aus dem Tombstone: der ueberlebt
das Verdichten, die Version davor womoeglich nicht.
Dabei die D76-Unschaerfe aufgeloest: RESTORED heisst nur noch "ein
geloeschtes Dokument ist wieder da" (der Client hebt seine Sperre auf), der
Rueckfall eines lebenden Dokuments ist ROLLED_BACK.
81 Tests. Gegenprobe: Schreibpause ignoriert -> genau die danach benannte
Zusicherung faellt; Rueckfall wieder als RESTORED -> Unit- und
Cucumber-Test dazu; juengster Stand aus der Historie genommen -> genau einer.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Anwenden, Berechnen, Rebasen und Pruefsumme in de.werkbaum.diff — ohne
Spring, damit die Regeln ohne Kontext pruefbar sind. Grundlage fuer
PATCH /content und den Aenderungsfeed (D76).
Beim Bauen entschieden, was das Konzept offenliess: Eine Einfuegung ist ein
Punkt ZWISCHEN den Zeilen und kollidiert nur mit dem Inneren eines fremden
Bereichs (start < index < end). Beide im Konzept genannten Folgen gelten
damit weiter — zwei Einfuegungen an derselben Stelle vertragen sich, eine
Einfuegung in einen geloeschten Bereich nicht —, aber die Raender bleiben
konfliktfrei: Wer eine Zeile ueber einer gerade geaenderten einfuegt,
bekommt keinen 409.
49 Tests. Gegenprobe: Rand-Regel auf halboffen mutiert -> genau die zwei
danach benannten Zusicherungen fallen; fremde Einfuegung an gleicher Stelle
nicht mitgezaehlt -> genau die eine.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Nachtrag 2 nannte "832 MB frei" - ein Schnappschuss, und dazu mehrdeutig
zwischen `free` und `available`. Nachgemessen: 326-358 MB free, 978-1004 MB
available von 3915 MB, dazu ~1 GB Swap belegt.
Falsch war ausserdem die Annahme, ein Managed Webspace koenne den Verbrauch
nicht zuordnen. `hidepid=invisible` verbirgt zwar fremde Prozesse, aber das
systemd-cgroup-Accounting (`systemctl status pacs-<paket>.slice`, so auch im
Hostsharing-Wiki) und die world-readable atop-Aufzeichnungen unter
/var/log/atop/ geben sie her.
Befund: nicht die Datenbanken. clamav-daemon 988 MB gegen mariadb 150 MB und
postgresql 111 MB; alle zwoelf Webspaces zusammen 100 MB. MariaDB haelt
602 MB im Swap - die DBs sind auf je 25 % des RAM provisioniert und verlieren
gegen den Virenscanner.
Fuers Deployment: pacs-mih00.slice hat MemoryMax=3147M - eine Erlaubnis, keine
Reservierung. `-Xmx` gehoert gegen das Freie bemessen; die JVM-Voreinstellung
(~980 MB) ist genau die Groesse, die MariaDBs Puffer verdraengt hat.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Der Zielserver bietet kein HTTP/2, also gilt das Browser-Limit von sechs
Verbindungen je Herkunft - ein dauerhaft offener Long-Poll je Tab engt bei
mehreren Tabs alles andere ein. Der Feed schliesst deshalb bei
visibilitychange und holt beim Zurueckkommen mit dem eigenen since nach.
Ein Hintergrund-Tab braucht keinen Live-Feed; das spart nebenbei
Server-Worker und Akku. Ein SharedWorker waere sauberer, ist aber eine eigene
Baustelle fuer ein Problem, das die einfache Loesung praktisch beseitigt.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
backend/CLAUDE.md sah Kotest-Assertions vor, der Code benutzte aber die
JUnit-Assertions. Umgestellt bei 25 Aufrufen in zwei Dateien - jetzt billig,
und Test-Abhaengigkeiten sind unkritisch, weil sie in keinem Artefakt landen.
Wo Kotest spezifischer ist, ersetzt es die handgeschriebenen Meldungen:
shouldContain statt assertTrue(contains, "..."), withClue nur noch dort, wo
der Kontext wirklich hilft.
Gegenprobe per Mutation: eine falsche Erwartung im Service-Test und eine im
BDD-Schritt lassen genau die danach benannten Tests fallen.
CLAUDE.md berichtigt: Das Backend ist nicht mehr "noch nicht bootstrapped",
die Konfiguration heisst application.yaml, die Schichten entsprechen dem
tatsaechlichen Aufbau, und die drei Spring-Boot-4-Fallen von heute sind
festgehalten, damit sie niemanden erneut treffen.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Das Backend-Geruest kam unter der Platzhalter-Wurzel com.example.editor
herein und widersprach backend/CLAUDE.md. Umgezogen nach de.werkbaum: 17
Kotlin-Dateien, Gradle-group, apiPackage/modelPackage der OpenAPI-Generierung,
die jacoco-Ausschluesse und das Cucumber-glue-Paket. Jetzt war es billig, mit
jeder Woche Entwicklung waere es teurer geworden.
Die BDD-Tests nutzen jetzt RestTestClient statt TestRestTemplate, das in
Spring Boot 4 als Auslaufmodell gilt. Weil Cucumber Senden und Pruefen
trennt, wird die fluent API nicht fuer Zusicherungen genutzt, sondern ueber
returnResult das Ergebnis festgehalten.
Nachgemessen: 22 Tests gruen, check inklusive Coverage-Verifikation besteht,
91,7 % Zeilenabdeckung, generierter Code weiterhin ausgeschlossen.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
RewriteRule [P] ist in der .htaccess erlaubt - gemessen mit einer temporaeren
Regel auf einen lokalen Testprozess, danach vollstaendig zurueckgebaut.
Sofortige Antwort HTTP 200 nach 0,13 s, absichtlich um 30 s verzoegerte
Antwort HTTP 200 nach 30,1 s: Apache haelt die Verbindung durch und puffert
nichts weg. Long Polling mit wait=25 ist damit gemessen tragfaehig, nicht nur
rechnerisch. Die Regel gehoert nach scripts/prod.htaccess, weil deploy-prod.sh
die Datei mit rsync --delete spiegelt.
JDK: eigenes 21 ins Home statt Toolchain auf die installierte 17 senken.
GraalVM Native Image ist vorgemerkt statt verworfen - es spart den Grossteil
des knappen RAM (nur 832 MB frei auf einem geteilten Server), scheitert aber
vorerst an der glibc-Differenz, an Liquibase/Hibernate-Metadaten und an einem
offenen Boot-4-Fehler fuer genau diese Kombination. Kotlin/Native scheidet
grundsaetzlich aus: Spring, Hibernate und JDBC sind JVM-Bibliotheken.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Die offenen Punkte der beiden Entwuerfe sind beantwortet und in beide
Dokumente eingearbeitet. Die wichtigsten Aenderungen am Protokoll:
- Der Server rebased selbst; 409 nur noch bei echter Ueberlappung. Reines
Ablehnen fuehrt zu Starvation - ein Client mit hoeherer Latenz kommt bei
fleissigen Mitschreibern womoeglich nie durch.
- Der Feed arbeitet auf der Historie statt am Dokument, sonst koennte er
ausgerechnet das Loeschen nicht melden, das er melden soll.
- Historie in zwei Ebenen: kurzlebige Sync-Versionen und nutzersichtbare
Meilensteine (nach Schreibpause und auf Knopfdruck, wie D54).
- Pruefsumme als Pflichtfeld, clientId + seq gegen doppelt angewendete
Patches, Volltext im Feed fuer zu alte Staende, eigener Aenderungstyp fuer
den Rollback, Titel im Feed-Ereignis.
- Zugriff ueber die unerratbare UUID; GET /documents bekommt ein
Master-Passwort (Spring Security).
Die Client-Instruktion war fuer ein anderes Frontend geschrieben: Sie nannte
CodeMirror, TypeScript, jsdiff und MSW. Werkbaum hat eine rohe textarea,
Vanilla JS und keine Laufzeit-Abhaengigkeiten - CodeMirror bleibt eine eigene
Entscheidung (gemessen: 120 kB gzip Einstieg, danach nur 18 kB fuer alles
Weitere).
Zielumgebung vermessen: Apache 2.4.68 mit MPM event, 1024 Worker,
Timeout 300 - Long Polling traegt dort. Offen bleiben fehlendes HTTP/2,
nur Java 17 statt 21 und der ungeklaerte Weg vom Apache zum Backend.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
- Liquibase-Schema (`document`, `document_history`) + Rollback-scripts
- Spring Boot with JPA-repositories, entities and services
- REST-API (`/documents`, `/documents/{id}`, `/documents/{id}/history`)
- OpenAPI-specifikation for CRUD-Operationen and history
- config files (`application.yaml`, `Liquibase`, H2 im PostgreSQL-Modus)
- preps for future live-editing/delta-updates
- Exceptions for conflikt- and not-found cases (409/404)
- keeping document hostory even after `delete` for RESTORE functionality
Beim Deploy der stabilen Instanz gehen diese Knoten live; SPEC §4
unterscheidet [x] fertig von [^] in Produktion (D30):
- #ed.deplink.fold: A hidden endpoint falls to its visible ancestor (S) %% source and target alike, see D75
- #ed.deplink.toggle: A switch hides the cross links
- #ed.fold.preset: The fold button cycles through four presets (S) %% M-and-smaller, off-path, all, none — see D44/D75
- Abhängigkeits-Kanten enden am nächsten sichtbaren Vorfahren, für Quelle
wie Ziel; zusammengefallene Kanten entfallen bzw. werden dedupliziert
(data-sub-ids/data-sub-deps am eingeklappten Vertreter, SPEC §9)
- Umschalter für die Querverbindungen neben dem Günstigster-Pfad-Knopf,
Voreinstellung an, persistiert; Export und Druck folgen ihm
- Der Falt-Knopf schaltet durch: M und kleiner zu → alles abseits des
Pfads zu → alles zu → alles offen; Icon/Tooltip zeigen den nächsten
Schritt, Handfaltung setzt die Position zurück (presetFoldSet, model.js)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Nur in Brave (navigator.brave) bekommt das Datei-Banner eine zweite Zeile
mit brave://flags/#file-system-access-api als kopierbarem Code-Text —
verlinken lassen sich interne Schemata nicht. i18n in neun Sprachen, die
Adresse selbst steht einmal im Code.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Brave ist Chromium-basiert, schaltet File System Access aber bewusst ab —
die Begründung „Chromium-Browser schreiben direkt zurück" widersprach dort
dem eigenen Erleben. Alle 18 UI-Strings (fsNotice + hint_save, 9 Sprachen)
nennen jetzt die Schnittstelle und Beispiel-Browser (Chrome/Edge).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Firefox/Safari laden Dateien als Kopie und speichern als Download — beim
ersten Öffnen/Speichern erklärt ein Banner die Grenze (i18n in neun
Sprachen, „Verstanden" merkt der localStorage, Reset räumt mit).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Mit bekanntem, aber nicht beschreibbarem Handle bekommt showSaveFilePicker
startIn + den exakten Dateinamen; ohne Handle teilen sich Öffnen und
Speichern eine Picker-id, damit der Dialog im Plan-Ordner aufgeht statt in
Downloads. Der Abbruch-Kreislauf (Dialog schlägt Falsches vor -> Abbruch ->
kein Handle gemerkt -> wieder Dialog) ist damit durchbrochen.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Beim Deploy der stabilen Instanz gehen diese Knoten live; SPEC §4
unterscheidet [x] fertig von [^] in Produktion (D30):
- #ed.files.ctrls: Ctrl+S saves the document as a file (XS) %% in place with a remembered handle
Die Geste, die jeder zuerst versucht, tat bisher das Falsche (Browser-Dialog
„Seite speichern"). Jetzt ruft sie saveLocalFile(): mit gemerktem Handle
dialogfrei in dieselbe Datei, sichtbar bestätigt durch einen Petrol-Haken am
Dokumentnamen; ohne Handle wie der Menü-Eintrag. Legenden-Zeile hint_save in
neun Sprachen. Nebenbefund behoben: adoptFile() wartet jetzt auf die aus
IndexedDB zurückgeholten Handles — ein Doppelklick unmittelbar nach dem
App-Start legte sonst ein Duplikat an statt dasselbe Dokument zu
aktualisieren.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Beim Deploy der stabilen Instanz gehen diese Knoten live; SPEC §4
unterscheidet [x] fertig von [^] in Produktion (D30):
- #bld.pwa: Installable as a progressive web app
- #bld.pwa.manifest: A manifest with name, icons and standalone display
- #bld.pwa.sw: A service worker that serves the app offline (S) %% network-first, cache as fallback
- #bld.pwa.update: The reload notice stays truthful under the worker (S) %% see D45/D73
- #bld.pwa.files: Double-click a .werkbaum file, save without any dialog (S) :#ed.files.inplace %% file_handlers + launchQueue
Manifest mit Icons aus der Marke und Standalone-Fenster; ein bewusst dummer
Service Worker beantwortet nur die App-Navigation network-first und hält die
zuletzt gesehene Fassung für den Offline-Fall — die Update-Prüfung (D45)
bleibt dadurch unverändert wahr, der skipWaiting-Lebenszyklus entfällt. Die
installierte App registriert sich für .werkbaum-Dateien (file_handlers +
launchQueue -> adoptFile). Beide Deploy-Wege kopieren die App-Hülle mit;
.htaccess liefert .webmanifest mit MIME-Typ aus.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Installierte App registriert sich für .werkbaum (file_handlers), Doppelklick
öffnet mit fertigem Handle aus der launchQueue, Speichern ohne Dialog —
hängt an der vorhandenen Stufe-2-Mechanik (:#ed.files.inplace).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Beim Deploy der stabilen Instanz gehen diese Knoten live; SPEC §4
unterscheidet [x] fertig von [^] in Produktion (D30):
- #ed.files: Open and save .werkbaum files
- #ed.files.inplace: Saving writes back into the opened file (S) %% File System Access, Chromium
File System Access API, wo vorhanden (Chromium): Öffnen merkt sich das
FileSystemFileHandle (IndexedDB, überlebt den Neustart), Speichern schreibt
in dieselbe Datei; dieselbe Datei öffnet per isSameEntry wieder in dasselbe
Dokument. Firefox/Safari behalten den Stufe-1-Download.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Zwei Einträge im Dokumenten-Menü: „Datei öffnen…" (Datei-Input, neues
Dokument, Dateiname als Name) und „Als Datei speichern" (Blob-Download,
Dateiname aus dem Dokumentnamen via localfile.js). Läuft in jedem Browser;
Stufe 2 (File System Access, Chromium) folgt.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Beim Deploy der stabilen Instanz gehen diese Knoten live; SPEC §4
unterscheidet [x] fertig von [^] in Produktion (D30):
- #ed.path.load: Warn when one person is the bottleneck (S) %% see D71
LLM oder Agentic AI mit eigenem API-Key anbinden, um den Plan im Dialog
zu bearbeiten; llms.md als Grundlage des System-Prompts.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Trägt ein @name mehr als die Hälfte der offenen Arbeit des günstigsten
Pfads (Marginalkosten-Maß, D69) und tragen mindestens zwei Personen Last
auf der offenen Front, meldet die zeilenlose Warnung assigneeOverload die
mögliche Engstelle; die Pillen der Person wechseln an offenen Pfad-Knoten
auf die Warnfarbe. Knoten ohne Tags erben für die Rechnung vom nächsten
getaggten Vorfahren, mehrere Tags teilen sich den Beitrag.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Beim Deploy der stabilen Instanz gehen diese Knoten live; SPEC §4
unterscheidet [x] fertig von [^] in Produktion (D30):
- #ed.path.cost: Cost model, a size prices its subtree
Die D66-Schätzung war status-blind: Ein größenloses Paket mit [x] (L) und
[ ] (S) wurde als L geschätzt, obwohl nur S offen ist. Erledigte Kinder
fallen jetzt heraus, eine erledigte realisierte Alternative stellt ihre
Gruppe fertig, und sind alle benannten Kinder erledigt, gilt XS (der eigene
Rest — M-Rückfall erhöhte den Preis beim Fertigwerden). Angegebene Größen
bleiben unangetastet (D69).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Die fast transparente Füllung mit mattgrauer Glyphe verschwand auf den
acht Status-Pastelltönen. Nur Farben geändert; line-height und Padding
bleiben (23-px-Geometrie).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Der Preis eines Knotens ist seine angegebene (oder D66-geschätzte) Größe;
die frühere Rekursion (eigene Größe plus Summe/Minimum der Kinder) zählte
doppelt und bestrafte sorgfältig zerlegte Pakete — eine zerlegte
S-Alternative verlor gegen ein grobes L. Das Vereinigungs-Maß der
Closure-Suche (D42) zählt je Knoten nur noch die Marge über seine nötigen
Teilpakete hinaus. Mitgelieferter Plan wählt identisch (per Vergleich
gemessen); SPEC §9 und llms.md nachgezogen.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Erkannt nur alleinstehend angesetzt (wie #id und :#…) — damit greifen die
Zitier-Konventionen "(L)" und ((L)) von selbst, und Backend(L) bleibt
Label. Das letzte Token gewinnt statt des ersten: Die Größe steht hinter
dem Titel, frühere Vorkommen sind Text. SPEC §1/§5 und llms.md nachgezogen;
mitgelieferte Beispiele unverändert (Parse-Vergleich alt/neu).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Vor dem `:#…`-Token prüfte depIdAt nur Space/Tab, der Parser jedes \s —
vor einem geschützten Leerzeichen war das Token damit eine Abhängigkeit,
auf die Strg+Klick nichts tat. Test mit echtem NBSP; Gegenprobe: die alte
Prüfung lässt genau ihn fallen.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Beim Deploy der stabilen Instanz gehen diese Knoten live; SPEC §4
unterscheidet [x] fertig von [^] in Produktion (D30):
- #ed.jump.dep: Ctrl+click follows a dependency to its id
Strg/Cmd+Klick auf eine Abhängigkeits-ID im Textfeld — auch jede ID der
Liste und die Kopf-Form #auth:#db — markiert die Zeile, die die ID vergibt;
Tastatur-Pendant Strg+Enter. Erste Vergabe gewinnt (D36/D39), unbekannte IDs
tun still nichts. Headless als depIdAt/idLine in autocomplete.js, 16 Tests;
Legenden-Zeile hint_jump in allen neun Sprachen erweitert.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Beim Deploy der stabilen Instanz gehen diese Knoten live; SPEC §4
unterscheidet [x] fertig von [^] in Produktion (D30):
- #ed.path.est: Missing size estimated from the sub-packages (S) %% see D66
`#ed.path.est` neu als [x]; das überholte „missing size counts as M" aus
Label und Beschreibung von `#ed.path.cost` entfernt. Der neue S-Knoten
kippte die Größenprüfung von `#ed.path` — ehrlich nachgezogen auf (XL).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Mindestens die größte Größe der zählenden Kinder, ab drei Kindern dieser
Größe eine Stufe mehr (Deckel XXL); disjunktiv die kleinste Alternative,
rekursiv über größenlose Kinder, Blatt bleibt M. Badge, Tooltip und
aria-label zeigen die geschätzte Größe („mindestens {size} angenommen").
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Lange Titel zogen das Diagramm zu breit. Ein Parameter in wrapLabel(),
der 32ch-Rückhalt im CSS und SPEC §9 ziehen mit; Balance-Regel und
Erste-Zeile-Verankerung unverändert.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Zwei Geometrie-Fehler bei Gruppen mit wenigen Kindern: :only-child schaltete
die Leiste ab, obwohl Stub (50 %) und Abzweig (23 px) seit dem 20-px-
Zusatzabstand nicht mehr zusammenfallen (6,6–15,8 px Lücke); und bei einem
großen Teilbaum im letzten Kind endete die Leiste über dem Stub (bis 98 px).
Fix: CSS-Verbinder 23px→50 % am Einzelkind, alignVRails() + --vrail-ext für
das letzte Kind. Export und Kompakt-Modus waren nie betroffen.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Beim Deploy der stabilen Instanz gehen diese Knoten live; SPEC §4
unterscheidet [x] fertig von [^] in Produktion (D30):
- #ed.render.wrap: Long titles wrap into balanced lines (S) %% max ~40 chars, see D64
- #ed.depcomplete: Typing :# suggests the known ids (S) %% arrows choose, Enter inserts
Der High-Risk-Status fehlte seit Juli in der Chip-Reihe der Legende;
hint_break nennt jetzt die Größen-Prüfung (D62), in allen neun Sprachen.
llms.md: Alignment-Marker an beiden Tabellen, die Größen-Bereiche als eigene
bündige Tabelle statt Prosa — inhaltlich war die Agenten-Fassung auf Stand
(Fortsetzungszeilen, ID-als-Titel, Größen-Konflikt, angefangene Zugaben).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Lange Knotentitel brechen bei ~40 Zeichen um, gleichmäßig auf die Zeilen
verteilt — vom Renderer (wrapLabel, headless getestet) statt text-wrap:balance,
damit der Kasten auf die längste balancierte Zeile schrumpft. Der
23-px-Abzweig ankert an der ersten Zeile; der Optional-Kreis sitzt fest auf
der Abzweighöhe. Der Grafikexport misst die gerenderten Zeilen (labelLines)
und gibt je Zeile ein <text> aus. Dazu: die eingeblendete ID (D56) steht in
einer eigenen Zeile über dem Titel, und das Falt-Zeichen ist ein gerahmter
19×16-px-Chip mit gedeckelter Höhe. Plan: #ed.render.wrap, #ed.render M→L
(D62-Prüfung schlug korrekt an).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Wer :# tippt, bekommt die vergebenen IDs als Liste an der Schreibmarke:
Präfix- vor Teilstring-Treffern, Knotentitel als Kontext, schon gelistete
und die eigene ID ausgenommen. ↑/↓ wählt, Enter/Tab übernimmt (undo-fähig),
Esc schließt; Live-Region für Screenreader. Eingabehilfe wie die Kurzform
(D55) — der Parser sieht nie etwas davon. Regeln headless in autocomplete.js
(20 Tests), app.js verdrahtet Popup, Tasten und Einfügen.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Liegt der Knoten der Cursor-Zeile in einem eingeklappten Teilbaum, hebt
nodeOfLine() den nächsten sichtbaren Vorfahren hervor — Cursor-Mitlaufen,
Alt+Klick, Alt+Enter und der Mobil-Umschalter erben es über die eine
Auflösungsstelle. Ausgeblendete verworfene Elemente heben weiterhin nichts
hervor (SPEC §9). Zeilen-Map als lineTargets() headless in model.js, 5 neue
Tests.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Beim Deploy der stabilen Instanz gehen diese Knoten live; SPEC §4
unterscheidet [x] fertig von [^] in Produktion (D30):
- #not.sizes: Size conflict — sub-packages must fit the given size (S) %% ranges, XXL open-ended, see 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>
`node_modules/.vite/vitest/.../results.json` lag seit a8c5e54 versehentlich
im Repo: Die .gitignore deckte nur `frontend/node_modules/` ab, nicht das
`node_modules/` in der Wurzel. Die Datei ändert sich bei jedem Testlauf und
rutschte dadurch still in fremde Commits.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Die Basis-Kanten lagen schon richtig; „drüber gemalt" wurde allein die
Hervorhebung des ausgewählten Knotens (`svg.dep-front`, z-index 4). Eine
durchgezogene Linie quer über einen fremden Knoten durchstreicht dessen
Titel — und beim Tippen wechselt die Hervorhebung mit jeder Zeile.
`dep-front` entfällt; hervorgehobene Kanten hängen zuletzt in derselben
hinteren Ebene, liegen damit über den übrigen Kanten und unter jedem
Knoten. Export und Druck waren nie betroffen.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>