Commit Graph
104 Commits
Author SHA1 Message Date
mhoennigandClaude Fable 5 842e89795a feat(taiga): Ticket-Stand im Knoten-Fenster — gelesen, nie geschrieben (D91-Nachtrag 6, SPEC §9)
Wo eine Ref steht und ein `&taiga.<slug>` gilt, zeigt das Knoten-Fenster
Betreff, Status und Zuständigen des Tickets; Taigas Statusname steht neben
der Statusbox der Notation (`In progress → [~]`).

- Proxy: zwei benannte Lese-Endpunkte (`GET /taiga/userstories/{ref}` und
  `…/tasks/{ref}`, je `?slug=`) — das Präfix der Ref trägt den Typ, Taiga
  hat getrennte `by_ref`-Endpunkte. Erst `/projects/by_slug`, dann `by_ref`
  (eine Ref ist nur je Projekt eindeutig); der Slug wird kodiert angehängt.
- Die Abbildung Status → Statusbox liegt im Editor (`mapTaigaStatus`,
  headless): Statuscodes sind Notation, das Backend parst sie nicht (D14).
  Unbekannte Namen bleiben unabgebildet — Raten hieße, dem Knoten eine
  Aussage zu geben, die niemand gemacht hat.
- Geholt wird erst nach 400 ms Verweilen und je Ticket einmal je Sitzung
  (↻ holt neu); ohne Anmeldung gar nicht — der Knopf meldet erst an.
- Nichts wird geschrieben: kein Text, keine Statusbox (das bleibt
  `#trk.write`).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-27 20:41:21 +02:00
mhoennig e58f71bf90 docs: Werkbaum-Plan — 2 Knoten in Produktion
Beim Deploy der stabilen Instanz gehen diese Knoten live; SPEC §4
unterscheidet [x] fertig von [^] in Produktion (D30):

- #trk.create: Create tickets from nodes
- #trk.open: Open a ticket ref in Taiga (S) :#trk.create.ref,#not.tag.project
2026-08-27 17:30:33 +02:00
mhoennigandClaude Fable 5 eae2ff1ef0 docs: D91-Nachtrag 5 — Ref-Umbruch und Strg+Klick-Öffnen festgehalten; #trk.create bestanden, #trk.open im Plan
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-27 17:28:36 +02:00
mhoennig 3f336db680 docs: Werkbaum-Plan — 7 Knoten in Produktion
Beim Deploy der stabilen Instanz gehen diese Knoten live; SPEC §4
unterscheidet [x] fertig von [^] in Produktion (D30):

- #not.tag.project: (&taiga.slug) assigns the Taiga project, inherited
- #trk.create.proxy: Backend proxy with named endpoints
- #trk.create.login: Log in to Taiga, token stays in the browser (S) :#trk.create.proxy
- #trk.create.project: The project comes from the inherited tag (S) :#not.tag.project
- #trk.create.story: A "create story" action in the node window (S) :#trk.create.login
- #trk.create.tasks: Sub-packages become tasks, picked in a dialog (M) :#trk.create.story
- #trk.create.ref: The ref lands in the line beside the id (S) :#trk.create.story
2026-08-27 16:54:26 +02:00
mhoennigandClaude Fable 5 d1de8758b1 feat(editor): Taiga-Story und -Tasks aus dem Knoten-Fenster anlegen (D91, #trk.create)
Zwei Aktionen im Knoten-Fenster, nur wo GET /info das Feature meldet
(Lebendprobe-Muster, je Basis einmal gecacht) und nur an Knoten ohne
Ticket-Referenz — die Ref an der Zeile ist der Idempotenz-Marker.
Login-Dialog (type=password, kein window.prompt; nur das Token landet
im Browser, 401 fragt neu), Projekt-Dialog vorbelegt aus dem geerbten
&taiga.<slug>, Häkchen-Dialog nach der D91-Vorbelegung; Refs und ggf.
das Projekt-Schlagwort gehen undo-fähig als Tokens an die Zeilen
(appendToken: vor %% und vor der Fortsetzungsmarke). Regeln headless in
taiga.js (15 neue Tests); Deploy zieht TAIGA_API_URL aus der .env als
WERKBAUM_TAIGA_API_URL in die Server-Umgebung nach (Nutzerwunsch).

E2E im Browser gegen Backend + Taiga-Stub gemessen: Buttons nur ohne
Ref, Vorbelegung Kundenprojekt aus geerbtem Tag, Refs #US-/#T- an den
richtigen Zeilen, Erledigtes uebersprungen, abweichende Projektwahl
schreibt &taiga.mi-intern zurueck, Undo/Redo je Schreibzug, falsches
Passwort bleibt als Taiga-Fehlertext im Dialog.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-27 16:51:10 +02:00
mhoennigandClaude Fable 5 fd0e730656 feat(backend): Taiga-Proxy — vier schmale Endpunkte, Ziel-URL aus der Server-Konfiguration (D91, #trk.create.proxy)
API First: /taiga/auth, /taiga/projects, /taiga/userstories, /taiga/tasks
in der OpenAPI-Spec; TaigaClient/TaigaProperties in
de.werkbaum.integration.taiga. Die API-URL kommt aus
WERKBAUM_TAIGA_API_URL (nie Request-Parameter — SSRF), das Token je
Aufruf im Header X-Taiga-Token (Authorization muessen OpenAPI-Werkzeuge
als Header-Parameter ignorieren) und geht als Bearer hinaus; der Server
speichert nichts und loggt keine Request-Bodies. Taiga-4xx werden samt
_error_message durchgereicht, 5xx/Netz sind 502, unkonfiguriert 503 —
und GET /info meldet das Feature (taiga). Tests gegen aufgezeichnete
Antwortformen auf einem JDK-HttpServer-Stub (statt WireMock: keine neue
Test-Abhaengigkeit, dieselbe Zusicherung); Gegenprobe: ohne den
type-Durchreich faellt genau der benannte Test. check gruen, 93 %
Coverage.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-27 16:33:35 +02:00
mhoennigandClaude Fable 5 5a18505571 feat(notation): Schlagworte &tag gebaut — taiga.<slug> benennt das Projekt je Teilbaum, vererbt (SPEC §1, D91)
SPEC zuerst: §1 Schritt 5b samt Block und Referenz-Regex, §11 auf
'umgesetzt' umgestellt; llms.md nachgezogen. Parser extrahiert
alleinstehende &-Token als node.marks (Zitier-Konvention gilt),
taigaSlugs() in model.js vererbt das taiga.*-Schlagwort wie die
@-Tags beim Engpass. Sichtbar im Tooltip und aria-label (a11yMarks,
9 Sprachen). Plan-Labels mit &-Erwähnungen eingeklammert; 12 neue
Tests, Gegenprobe: ohne Alleinstehend-Anker fallen genau die zwei
Zitier-Tests.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-27 16:20:03 +02:00
mhoennigandClaude Fable 5 c712e81970 docs: Mehrprojekt-Pläne — &taiga.slug erbt je Teilbaum, erster &tag-Konsument (D91-Nachtrag 3, SPEC §11)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-27 14:48:03 +02:00
mhoennigandClaude Fable 5 4c98ec6603 docs: Ticket-Ref zusätzlich zur Knoten-ID, Präfixe US-/T- festgelegt (D91-Nachtrag 2, SPEC §11)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-27 14:37:08 +02:00
mhoennigandClaude Fable 5 6bbc91e849 docs: Ticket-Anlage aus Knoten festgelegt — Proxy, zwei Aktionen, Häkchen-Dialog (D91, #trk.create)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-27 14:16:41 +02:00
mhoennig 0d573a80c4 docs: Werkbaum-Plan — 2 Knoten in Produktion
Beim Deploy der stabilen Instanz gehen diese Knoten live; SPEC §4
unterscheidet [x] fertig von [^] in Produktion (D30):

- #col.git.pull: A script pulls the server document and commits it (S)  %% tools/pull-doc --git-commit, dated commits — see D88
- #col.live.safe: Unsent work is loud, rescued and never silently discarded (M)  %% modal for a second window, watchdog, local backups — see D89
2026-08-27 13:04:58 +02:00
mhoennigandClaude Fable 5 408e23f25b feat(live): vier Netze gegen stillen Verlust — Zwei-Fenster-Modal, Wachhund, Rettungs-Sicherungen, lokale Stände für Server-Dokumente (D89)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-27 12:51:11 +02:00
mhoennigandClaude Fable 5 42d37daf63 feat(tools): pull-doc committet nur noch mit --git-commit (D88-Nachtrag)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-27 12:00:53 +02:00
mhoennigandClaude Fable 5 887b5c75ee feat(tools): pull-doc holt ein Server-Dokument in ein git-Worktree und committet es datiert (D88)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-27 11:56:12 +02:00
mhoennigandClaude Fable 5 22769cf3b6 docs: Nutzen je Knoten festgelegt — (M/9) im Größen-Token, lohnendster Pfad (SPEC §11)
Geplant, nicht gebaut: Ziffer 0-9 im Größen-Token (0 = Vorbedingung),
reine Autoren-Aussage ohne Aggregation; der Pfad-Knopf wird ein
Durchschalter aus -> günstigster -> lohnendster. Plan-Knoten
#not.benefit (XOR-Gruppe aufgelöst) und #ed.path.worth.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-27 11:03:36 +02:00
mhoennig d12bfd1c57 docs: Werkbaum-Plan — 3 Knoten in Produktion
Beim Deploy der stabilen Instanz gehen diese Knoten live; SPEC §4
unterscheidet [x] fertig von [^] in Produktion (D30):

- #ed.lens: A people bar with a person lens
- #ed.lens.fold: One person's nodes, everything else folds (S)  %% view-only, never written into the text
- #ed.lens.load: A load bar per person, shares of the open path work (S)  %% the D71 measure
2026-08-27 10:41:07 +02:00
mhoennigandClaude Fable 5 deec39e82a feat(diagram): Personen-Leiste mit Personen-Linse und Belastungsanzeige (D87)
Je @Person eine Pille mit Anteil an der offenen Pfad-Arbeit (D71-Maß),
dazu ein Eintrag für Unzugewiesenes — die Anteile summieren auf 100 %.
Ein Tipp filtert das Diagramm auf die Knoten der Person: alles andere
faltet, ihre Pakete stehen als ▸ n, die Pillen färben petrol. Die Linse
ist rein Ansicht und Sitzungssache — in den Text wird nichts geschrieben,
in geteilten Dokumenten ginge der persönliche Filter sonst an alle.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-27 10:38:53 +02:00
mhoennigandClaude Fable 5 67d3585310 docs: Text-Falten im Editor als Idee in den Plan (#ed.textfold, unbewertet)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-27 10:14:12 +02:00
mhoennig f6c7eff490 docs: Werkbaum-Plan — 3 Knoten in Produktion
Beim Deploy der stabilen Instanz gehen diese Knoten live; SPEC §4
unterscheidet [x] fertig von [^] in Produktion (D30):

- #col.live.rename: Renaming reaches everyone (S)  %% PATCH /title + RENAMED in the feed, see D85
- #col.live.history: Earlier states come from the server (M)  %% milestones in the clock menu, rollback for everyone, see D86
- #col.live.name: A self-chosen display name (XS)  %% fills "changed by", a claim, not a proof
2026-08-27 08:36:15 +02:00
mhoennigandClaude Fable 5 53b48bcd42 feat(paket c): Frühere Stände eines geteilten Dokuments kommen vom Server (D86)
Der Verlaufs-Knopf zeigt bei Geteilten die Meilenstein-Historie des
Servers (GET /history, jetzt samt clientId/displayName); Laden ist ein
Server-Rollback (POST /restore, ROLLED_BACK — neue Version für alle,
nichts geht verloren, mit Rückfrage). Die Kamera legt einen
Server-Meilenstein an (pushLive(true), leeres Diff erlaubt); lokale
Momentaufnahmen sammelt snapshotNow() für Server-Dokumente nicht mehr —
sie enthielten fremde Arbeit und überschrieben sie beim Laden als
eigenes Diff. Dazu der Anzeigename: einmal gefragt, im Browser gemerkt,
füllt das 'geändert von' der Historie (Behauptung, kein Nachweis).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-27 08:21:17 +02:00
mhoennigandClaude Fable 5 fbe9b1fd22 feat(paket b): Umbenennen erreicht alle — PATCH /title und RENAMED im Feed (D85)
Backend: PATCH /documents/{id}/title mit expectedVersion (409 bei
veralteter Version, 400 bei leerem/zu langem Titel), neuer ChangeType
RENAMED (strukturell, immer Meilenstein), der Feed stellt den neuen Titel
im Klartext zu (ChangeEvent.title). Unter derselben Stripe-Sperre wie die
Inhalts-Patches; Owner-Vormerkung in der API-Beschreibung. Vier neue
Cucumber-Szenarien, zwei Unit-Tests.

Frontend: Der Zeilen-Stift eines Server-Dokuments benennt über den Server
um (optimistisch, 409-Retry, Rücknahme + Warnung bei Fehlschlag); fremde
Umbenennungen kommen als RENAMED über den Feed in Chip und Menü.
URL-Dokumente verlieren den Stift — ihr Name ist die URL.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-27 08:13:25 +02:00
mhoennigandClaude Fable 5 83dc329b43 docs(plan): Owner-Passwort für geteilte Pläne als geplant eingetragen (#col.live.owner)
Beim Anlegen einmalig vom Server geliefert, Browser hält eine Kopie, keine
Recovery auf fremdem Browser; Verwaltung bindet künftig daran, ein Admin
mit Master-Passwort kann reclaimen. Nur Plan-Eintrag, noch keine Umsetzung.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-27 07:44:13 +02:00
mhoennigandClaude Fable 5 5c8fbcde01 feat(menü): Geteilte werden verlassen, nicht gelöscht (D81-Nachtrag 5)
Eigene Aktion für die Geteilt-Gruppe (Server- und URL-Dokumente): Wort
(Verlassen), Icon (Tür mit Pfeil statt Papierkorb), Rückfrage (nur der
eigene Listeneintrag geht, Server bleibt) und normales statt rotem Hover.
Der Link wandert beim Verlassen in die Zwischenablage — der Listeneintrag
ist zugleich das Lesezeichen. Verhalten wie lokales Löschen
(removeDocLocally, gemeinsamer Kern); i18n in neun Sprachen.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-27 07:28:43 +02:00
mhoennigandClaude Fable 5 a78147e459 feat(menü): Gruppen heißen Mitgeliefert · Lokal · Geteilt (D81-Nachtrag 4)
Nutzer-Wortwahl statt „Eigene"/„Quellen": Lokale leben nur in diesem
Browser, Geteilte (Server- und URL-Dokumente) erreicht man über eine
weitergegebene Adresse. In allen neun Sprachen.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-27 07:06:00 +02:00
mhoennigandClaude Fable 5 ca4c6f98d8 fix(menü): mitgelieferte Dokumente sind nicht mehr umbenennbar (D81-Nachtrag 3)
Ihr Name ist Auslieferungsstand: Wiederherstellen stellt ihn mit her, der
Neu-laden-Vergleich prüft ihn, und ein umbenanntes, aber unverändertes
Beispiel bekäme weiter still neue Fassungen nachgezogen (das Nachziehen
hängt an id + Text-Fingerabdruck, nicht am Namen). Kein Stift an
mitgelieferten Zeilen; renameDoc() prüft es selbst. Löschen bleibt.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-27 05:51:41 +02:00
mhoennig 4e200e4d9b docs: Werkbaum-Plan — 2 Knoten in Produktion
Beim Deploy der stabilen Instanz gehen diese Knoten live; SPEC §4
unterscheidet [x] fertig von [^] in Produktion (D30):

- #ed.docs.picker: Breadcrumb picker in the app header (S)  %% Werkbaum › name, see D81
- #col.live.upload: Share: put a local plan on the server (S)  %% a button in the editor title bar, see D81
2026-08-27 05:42:24 +02:00
mhoennigandClaude Fable 5 ed81a2fe41 fix(menü): Wiederherstellen nur noch über den Neu-laden-Knopf der Titelzeile (D81-Nachtrag 2)
Das Zeilen-Symbol im Dokumenten-Menü konnte ein UNGEÖFFNETES mitgeliefertes
Dokument zurücksetzen — ohne zu sehen, was man verwirft (Nutzer-Einwand).
Umbenennen und Löschen bleiben je Zeile; restoreDoc() wirkt nur noch auf das
geöffnete Dokument und prüft das selbst.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-27 05:33:34 +02:00
mhoennigandClaude Fable 5 4be6ecb984 feat: Dokumenten-Menü neu — Brotkrume im App-Kopf, Stand-Knöpfe im Editor-Kopf (D81)
Der Dokument-Wähler steht jetzt als gerahmter Chip "Werkbaum › Name" im
App-Kopf und ist damit auch auf dem Telefon aus beiden Bereichen erreichbar;
sein Name übernimmt dort die Untertitel-Zeile. Das Menü gruppiert nach
Dokumentart (docKind, headless getestet), trägt Umbenennen/Löschen/
Wiederherstellen als Symbole je Zeile und scrollt als Ganzes, statt auf
kleinen Bildschirmen abgeschnitten zu werden. Die Editor-Titelzeile heißt
wieder "Text-Editor" und trägt die Stand-Funktionen des aktiven Dokuments:
Speichern (Strg+S), Stand sichern, frühere Stände, Neu laden (Original/URL/
Datei) und den neuen kurzen Teilen-Knopf (vorher "Auf den Server legen" im
Menü). subtitleShort ist ersatzlos ausgebaut.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-26 22:21:11 +02:00
mhoennig bfacbe96e5 feat: Etherpad-Anbindung ausgebaut (D78)
D31 lieh sich fuer die Echtzeit-Zusammenarbeit ein Etherpad: Das Pad war die
Schreibflaeche, Werkbaum die Ansicht. Richtig fuer den Zeitpunkt - die schwere
Arbeit war dort getan, und Werkbaum hatte kein Backend. Jetzt hat es eins
(D76), das dasselbe besser und im Editor selbst kann.

D31 bleibt stehen: Was dort gemessen wurde, ist der Grund, warum die Anbindung
nie gut wurde - Export-Drosselung (10 je 90 s), SameSite=Lax beim
Autoren-Cookie, abgelehnter Socket-Upgrade. Uebrig blieb ein Neu-laden-Knopf
und ein schreibgeschuetztes Textfeld.

Ein alter ?etherpad=-Link meldet sich, statt still nichts zu tun: Der Parameter
bleibt erkannt und ergibt die zeilenlose Warnung padGone, die auf ?live= zeigt
(neun Sprachen). Geholt wird nichts - nachgemessen geht keine Anfrage mehr an
den Pad-Host. Vorhandene Pad-Dokumente bleiben liegen und werden dabei besser:
ohne Schreibschutz sind sie bearbeitbar und sammeln fruehere Staende.

Mit ausgebaut, weil sie nur der Anbindung dienten: remote.js samt Tests, der
Ansichts-Waehler und sein Splitter, der Neu-laden-Knopf samt
Drosselungs-Zaehler, die Warnungen sourceTimeout/padRateLimit, elf i18n-
Schluessel x 9 Sprachen - und src.readOnly ganz, denn nur Pad-Dokumente haben
es gesetzt (mit ihm updateSnapBtn und sieben tote Waechter). Auch #srcArea
faellt weg: den Kasten gab es nur, damit Pad-Rahmen und Spiegel sich einen
Bereich teilen. ?sourceUrl= bleibt unangetastet - beide teilten sich einen
Fetch-Pfad, das war die Stelle, an der nichts verrutschen durfte.

Im Plan wird aus dem #col.pad-Zweig (14 Knoten samt der ganzen
"Update by itself"-Gruppe) ein verworfener Knoten mit Begruendung; der
gemeinsame Zeigefinger wandert als #col.point heraus, denn !!! gilt jedem
geteilten Dokument. 196 Knoten, 0 Warnungen.

Nebengewinn: Der README bekommt endlich den Abschnitt zu ?live= - den gab es
nie, die Einstiegs-Beschreibung stand weiter beim Pad.

Nachgemessen im Browser: ?etherpad= zeigt die Warnung ohne Netz-Anfrage,
?sourceUrl= laedt unveraendert und ist beschreibbar, Textfeld und
Zahlenstreifen liegen buendig, Legenden-Splitter teilt wie zuvor (954+10+300),
Falten und Staende funktionieren. 501 Tests, 946 Zeilen weniger.
2026-08-26 20:31:33 +02:00
mhoennig 911a100df4 docs: Werkbaum-Plan — 2 Knoten in Produktion
Beim Deploy der stabilen Instanz gehen diese Knoten live; SPEC §4
unterscheidet [x] fertig von [^] in Produktion (D30):

- #bld.backend.remote: One command for everything on the server (S)  %% remote <target> <action>
- #bld.backend.backup: Save the database, and prove it can be read back
2026-08-26 20:14:55 +02:00
mhoennig ac6df1bb6b feat(tools): remote <ziel> <aktion> als Vordertuer zum Server (D77-Nachtrag)
Deployen, Log ansehen, Dienst schalten und fragen was laeuft waren vier
verschiedene Beschwoerungen, drei davon von Hand als ssh + systemctl. Jetzt:

  remote backend deploy|upload|setup|install-jdk|reset-password
  remote backend start|stop|restart|status|enable|disable
  remote backend log|info|documents|backup
  remote frontend deploy|preview|info
  remote ssh

Die Skripte unter scripts/ bleiben die Implementierung und einzeln aufrufbar;
tools/remote bringt nur mit, wofuer es bisher nichts gab. Wo ein Schalter
noetig war, kam er ins Skript statt ins Werkzeug: --unit-only in
deploy-backend.sh (sonst kennte eine zweite Stelle die Unit-Platzhalter) und
--dry-run in deploy-prod.sh, das die Befoerderung ausdruecklich mit abschaltet.

Neu ist die Sicherung: H2 haelt die Datei offen, also anhalten, holen, wieder
starten (gemessen ~8 s Auszeit) - und das Archiv lesen, bevor der Befehl es
behaelt. Gegenprobe von Hand: lokal ausgepackt, Backend mit --werkbaum.data-dir
dagegen gestartet, es liefert genau die Dokumente des Servers.

.envrc legt tools/ auf den PATH (direnv), 217 Plan-Knoten, 0 Warnungen.
2026-08-26 19:59:24 +02:00
mhoennig 9443790382 docs: Werkbaum-Plan — 2 Knoten in Produktion
Beim Deploy der stabilen Instanz gehen diese Knoten live; SPEC §4
unterscheidet [x] fertig von [^] in Produktion (D30):

- #be.scaffold.info: An endpoint that says name and version
- #col.live.upload: Put a local plan on the server, from the menu
2026-08-26 19:40:01 +02:00
mhoennigandClaude Opus 5 ea1979d567 feat(frontend): "Auf den Server legen" im Dokumenten-Menue (D76-Nachtrag 8)
Bis hierher kam ein Plan nur per curl auf den Server — der Menueintrag war im
Konzept vorgesehen und fehlte. Jetzt legt der Knopf das aktive Dokument an,
schaltet dorthin um und schreibt den Link in die Adresszeile und in die
Zwischenablage. Die Adresszeile IST der Link: dort sucht man ihn, und ein
Neuladen fuehrt ins selbe Dokument zurueck.

Die Basis-Adresse ist die eigene Herkunft — produktiv liegt das Backend hinter
derselben Domain, wer nichts konfiguriert bekommt also das Richtige. Darueber
liegen ?server= (Entwicklung) und die Adresse des offenen Server-Dokuments.
Traegt nichts, wird gefragt.

Das lokale Dokument bleibt: Wer sein einziges Exemplar einem Server
anvertraut, soll es nicht im selben Zug verlieren. Dafuer nennt der Waehler
jetzt den Host neben Server-Dokumenten — im Test standen sonst zwei Eintraege
"Nur lokal" da, unterscheidbar nur am Tooltip. Und der Knopf verschwindet bei
Dokumenten, die schon auf einem Server liegen.

Geprueft im Browser gegen ein echtes Backend: Knopf sichtbar bei einem lokalen
Dokument, nach dem Klick steht ?live=... in der Adresszeile, das lokale
Dokument ist noch da, der Knopf verborgen — und eine getippte Zeile erreicht
den Server als Version 2.

523 Frontend-Tests (5 neue fuer serverBase/documentsUrl).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-26 19:39:28 +02:00
mhoennigandClaude Opus 5 dea904814b fix(backend): Neustart gegen die eigene Datenbank, /info als Lebendprobe
Der Dienst lief einmal und stuerzte danach in einer Schleife: MODE=PostgreSQL
laesst H2 unquotierte Bezeichner klein anlegen, Liquibase sucht seine
Verwaltungstabellen gross, findet nichts, legt sie an — "Table
databasechangelog already exists". Der erste Start ging, jeder weitere nicht.
Gemessen mit dem echten Jar: ohne den Modus laufen beide. Liquibase auf
Kleinschreibung zu konfigurieren half nicht, es korrigiert den Namen selbst
zurueck — deshalb weicht der Modus ganz.

Die Testsuite konnte das nicht finden (jeder Test bekommt eine frische
In-Memory-DB), und der Regressionstest dafuer hat zweimal gelogen: erst reichte
er die URL als Default-Property herein, die die application.yaml ueberstimmt;
dann als Argument, aber damit pruefte er eine URL, die er sich selbst
ausgedacht hatte. Jetzt hat die URL einen Regler (werkbaum.data-dir), der Test
ueberschreibt nur den, und die Gegenprobe faellt.

Dieselbe Sorte Fehler eine Ebene hoeher: Die Testkonfiguration hiess
application.yaml und verdeckte damit die Hauptkonfiguration vollstaendig. Sie
ist jetzt eine Profil-Ueberlagerung.

Dazu GET /api/v1/info mit Name, Version und Bauzeitpunkt. Die Lebendprobe
erwartete bisher eine 404 von einem Dokument, das es nicht gibt — ein
erwarteter Fehler ist eine schlechte Zusicherung, dieselbe 404 liefert auch ein
falsch konfigurierter Proxy.

138 Backend-Tests. Gegenprobe: MODE=PostgreSQL zurueck -> genau der
Neustart-Test faellt.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-26 19:02:51 +02:00
mhoennig 0ed757a80a docs: Werkbaum-Plan — 18 Knoten in Produktion
Beim Deploy der stabilen Instanz gehen diese Knoten live; SPEC §4
unterscheidet [x] fertig von [^] in Produktion (D30):

- #bld.backend: Ship the backend as a service of its own
- #bld.backend.jdk: Its own JDK 21 in the server's home (S)  %% only 17 is installed there
- #bld.backend.unit: A systemd user unit that survives the session
- #bld.backend.proxy: Apache passes /api/ on, in .htaccess (XS)  %% measured: the P flag is allowed
- #be.scaffold.gradle: Gradle Kotlin DSL, JDK 21
- #be.docs: Documents on the server (L) :#be.scaffold
- #be.docs.model: Text plus metadata, no structure format
- #be.docs.rest: REST endpoints
- #be.docs.db: Persistence
- #be.docs.history: History in two layers, and restore
- #be.docs.share: Share by link
- #be.docs.list: Master password for the document list
- #col.live.http: Transport over plain HTTP (M) :#be.docs  %% no websocket needed
- #col.live.patch: Submit a change as a line diff
- #col.live.feed: Long polling holds the other direction open
- #col.live.merge: Merging simultaneous edits (L)  %% the actual work
- #col.live.diff: Line diffs against a base version; the server rebases
- #col.live.ask: On a real overlap, ask whose version wins
2026-08-26 18:29:39 +02:00
mhoennigandClaude Opus 5 4aff995a0e feat(deploy): Backend auf die stabile Instanz bringen (D77)
Drei Teile, die das Frontend-Deploy nicht braucht: ein eigenes JDK 21 im Home
des Servers (dort ist nur 17 installiert, und die Toolchain zu senken hiesse,
Entwicklung und Produktion auseinanderlaufen zu lassen), ein systemd-User-Unit
statt nohup, und die Proxy-Regel in der .htaccess — gemessen ist, dass das
P-Flag auf diesem Hoster erlaubt ist und eine Verbindung 30 s durchhaelt.

Der Port steht an genau einer Stelle: deploy-prod.sh setzt ihn in die
Proxy-Regel, deploy-backend.sh in die Unit. Zwei Zahlen, die zueinander passen
muessen, sind eine zu viel.

Die JVM-Flags sind gemessen, nicht geschaetzt. Mein erster Entwurf setzte
-Xmx384m; nachgemessen kam heraus, dass die Obergrenze der kleine Hebel ist:
Ohne Freiraum-Verhaeltnisse behaelt der Kollektor den gewachsenen Heap, obwohl
nach einem GC nur ~45 MB leben. Mit ihnen 174 MB RSS statt 291 MB ohne jede
Angabe — auf einem Host mit rund 300 MB frei ist das der Unterschied zwischen
"passt" und "draengt die Datenbank weiter in den Swap".

Zwei Fallen sind eingebaut, weil beide nur am Ziel auffielen: systemd
expandiert kein $HOME (deshalb %h), und `systemctl --user` findet ohne
XDG_RUNTIME_DIR seinen Manager nicht.

Geprueft bis an die SSH-Grenze: Das Jar startet mit genau den Flags der Unit
in einer Sekunde, antwortet auf die Lebendprobe mit 404 und ist von aussen
nicht erreichbar; die erzeugte Unit besteht systemd-analyze verify; der ganze
Ablauf lief mit gestelltem ssh/rsync durch; deploy-prod.sh liefert die
Proxy-Regel mit eingesetztem Port aus. Der Deploy selbst laeuft erst, wenn
jemand ihn startet.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-26 18:11:56 +02:00
mhoennigandClaude Opus 5 ec6fd452b7 docs: der mitgelieferte Plan sagt, was wirklich gebaut wurde
Live-Editing stand dort als Websocket plus CRDT — gegangen sind wir einen
anderen Weg: Zeilen-Diffs ueber gewoehnliches HTTP, der Server verschiebt
selbst, und nur bei echter Ueberschneidung entscheidet ein Mensch. Der
Websocket-Knoten ist damit verworfen, nicht offen; die neue Antwort steht als
dritte Alternative neben CRDT und OT.

Dazu der Backend-Teil: Geruest, Dokumente, Historie, Teilen per Link und der
Passwortschutz der Liste sind fertig. `[x]`, nicht `[^]` — befoerdert wird
beim Deploy (D30).

209 Knoten, alle mit ID und Beschreibung, 0 Warnungen.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-26 17:50:13 +02:00
mhoennig 4e0ce51820 docs: Werkbaum-Plan — 3 Knoten in Produktion
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
2026-08-25 17:20:48 +02:00
mhoennigandClaude Fable 5 33443016ce feat: Querverbindungen folgen der Faltung + Schalter; Falt-Knopf mit vier Voreinstellungen (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>
2026-08-25 17:19:32 +02:00
mhoennig 532bbace21 docs: Werkbaum-Plan — 1 Knoten in Produktion
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
2026-08-25 15:18:42 +02:00
mhoennigandClaude Fable 5 72ab9746d5 feat: Strg+S speichert das Dokument direkt als Datei (D74)
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>
2026-08-25 15:15:50 +02:00
mhoennig 45a52c15f9 docs: Werkbaum-Plan — 5 Knoten in Produktion
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
2026-08-25 15:05:20 +02:00
mhoennigandClaude Fable 5 19053eba1d feat: Als PWA installierbar — Manifest, Offline-Worker (network-first), Dateihandling (D73)
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>
2026-08-25 15:03:22 +02:00
mhoennigandClaude Fable 5 f6785e3138 docs: PWA-Dateihandling als Plan-Knoten (#bld.pwa.files)
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>
2026-08-25 14:46:24 +02:00
mhoennig c00ff75346 docs: Werkbaum-Plan — 2 Knoten in Produktion
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
2026-08-25 14:32:01 +02:00
mhoennigandClaude Fable 5 2648d72e45 feat: Speichern schreibt in die geöffnete Datei zurück (D72-Nachtrag, Stufe 2)
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>
2026-08-25 14:31:51 +02:00
mhoennigandClaude Fable 5 defeaf84d0 feat: Lokale Dateien öffnen und als Datei speichern (D72, Stufe 1)
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>
2026-08-25 14:26:31 +02:00
mhoennig ced3507678 docs: Werkbaum-Plan — 1 Knoten in Produktion
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
2026-08-25 11:32:07 +02:00
mhoennigandClaude Fable 5 ea25e87831 docs: Werkbaum-Plan — AI-Integration als Idee notiert (#ai)
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>
2026-08-25 11:31:46 +02:00
mhoennigandClaude Fable 5 35b72fc178 feat: Zuständigen-Engpass — Warnung, wenn eine Person mehr als die Hälfte des Pfads trägt (D71)
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>
2026-08-25 11:29:52 +02:00