Beim Deploy der stabilen Instanz gehen diese Knoten live; SPEC §4
unterscheidet [x] fertig von [^] in Produktion (D30):
- #bld.werkator: Also built by Werkator, the project's own CI (S) %% .werkator.yml, backend + frontend
- #bld.gitea: The repository lives on Gitea, GitHub stays a clone
Zuhause ist künftig git.javagil.de/mi/werkbaum (origin); GitHub bleibt unter
dem Remote `github` ein Klon, den scripts/push-github.sh mit `main` versorgt —
nur vorwärts, nie erzwungen.
Der Klon bleibt aus zwei gemessenen Gründen: Gitea sendet auf raw-Dateien kein
Access-Control-Allow-Origin (nachgemessen an mi/werkdock), die
?sourceUrl=-Beispiellinks der READMEs müssen also auf raw.githubusercontent.com
zeigen; und der Pages-Workflow lässt sich nicht mitnehmen.
Footer, llms.txt und llms.md zeigen jetzt auf Gitea (Repo-Seite ohne Anmeldung
lesbar, HTTP 200 geprüft); beide COMMIT_URL-Stellen (deploy-prod.sh und der
Pages-Workflow) ziehen mit. deploy-prod.sh prüft origin und github getrennt und
erinnert an das Spiegeln, statt selbst zu pushen.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Beim Deploy der stabilen Instanz gehen diese Knoten live; SPEC §4
unterscheidet [x] fertig von [^] in Produktion (D30):
- #ed.docs.windows: Two windows on one storage: app and tab side by side (M) %% docs/rfc/002-mehrfenster.md, D94
- #ed.docs.windows.keys: Every document owns its keys — text, meta, states; the index never deletes
- #ed.docs.windows.sync: The storage event keeps the list, names and tombstones current in every window
- #ed.docs.windows.lock: A per-document lock via the Web Locks API finds the one real loss case (S) :#ed.docs.windows.keys
- #ed.docs.windows.dialog: Three exits instead of continue anyway: open another, view only, edit anyway (XS) :#ed.docs.windows.lock
Kein Fenster schreibt mehr den Schlüssel eines anderen: Text, Meta und
frühere Stände liegen je Dokument unter eigenen Schlüsseln, der Index ist
nur noch Reihenfolge-Hinweis und löscht nie, Löschungen hinterlassen einen
Tombstone (7 Tage). Der Voll-Flush weicht dem Dirty-Flush. Damit sind
Befund 2 und 3 des RFC weg — verschiedene Dokumente in zwei Fenstern
kollidieren gar nicht mehr. docsync.js zieht Liste, Namen, Tombstones und
den Stände-Cache aus dem storage-Ereignis nach; am aktiven Dokument die
drei Fälle umbenannt / anderswo gelöscht (behalten, bis getippt wird) /
fremd geschrieben. Der modale D89-Dialog samt Präsenz-Kanal ist ersatzlos
raus; den einen Verlustfall — dasselbe nicht-`live:`-Dokument in beiden
Fenstern vorn — findet eine Sperre per Web Locks, und das zweite Fenster
bekommt drei Auswege statt „trotzdem fortfahren".
Im Browser mit zwei echten Tabs nachgemessen (RFC §10): Fälle 1–8 grün,
Fall 1 im Vor-v3-Build gegengeprüft und dort nachweislich rot. Fall 9
deckte auf, dass reviveGoneDoc() den Index-Hinweis nicht mitschrieb — ein
durch Tippen wiederbelebtes Dokument hing bis zum nächsten Flush allein an
seinen eigenen Schlüsseln und wäre bei einem Rückbau auf einen Build vor
v3 still verlorengegangen; behoben, Gegenprobe per Mutation gezogen.
Headless 666 Tests grün. Offen bleibt die Handarbeit: PWA neben Tab,
Firefox, Safari, ein Browser ohne Locks-API.
SPEC §9, D94, D89-Nachtrag, RFC (Status, §10, Revisionsgeschichte),
CHANGELOG und Plan-Knoten #ed.docs.windows nachgezogen.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Der modale Zwei-Fenster-Dialog (D89) verbietet den Fall, der funktioniert
(dasselbe geteilte Dokument in App und Tab), und schützt nicht vor dem,
wovor er warnt: Der Voll-Flush löscht Dokumente und Stände des anderen
Fensters. Entschieden: je Dokument eigene Schlüssel, Tombstones, Sperre je
Dokument per Web Locks, Dialog mit drei Auswegen. Nichts gebaut.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Nutzer-FYI: Node.js einzuführen ist eine Dimension schwerer als eine
Backend-Abhängigkeit und muss dem Entwickler deutlich gemacht werden —
als Regel in CLAUDE.md. Aus den nachgefragten Beweggründen folgt: kein
lokaler Prozess. Der MCP-Server lebt im Backend (Streamable HTTP,
Bearer-Token), führt die Frontend-Module per GraalJS aus, jedes Werkzeug
hat eine text- und eine document-Form; das Node-Paket ist verworfen,
seine Tool-Schicht lebt als frontend/src/inspect.js, guard.js, edit.js.
Backend-Abhängigkeiten GraalJS + Spring-AI-Starter zugestimmt; der
GraalJS-Spike steht vor allem anderen. RFC neu geschrieben (§12 hält die
drei Runden fest), Plan-Teilbaum #ai.mcp neu, D93-Nachtrag.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Der erste Entwurf nahm an, MCP erzwinge Node; tatsächlich erzwang es nur
der Parser. Das Backend darf den HTTP-Transport tragen, wenn es die
JS-Module per GraalJS ausführt (dieselbe Technik wie #idea.drift.js) —
Kotlin ist Transport und Zugang, die Logik bleibt einmal vorhanden;
Rückfall ist der Node-Dienst hinter Apache. Urheber im Git-Spiegel:
Historie im Rumpf plus Trailer statt git --author, damit blame nicht lügt.
Entscheidungen in RFC §11 und D93 (zweite Runde).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Ein Node-Paket, das die headless-Module des Frontends unverändert
importiert (kein zweiter Parser, D14), den Plan als Ressource und Tools
anbietet (Baum, Warnungen, günstigster Pfad, Knoten-Verben) und
Änderungen als Zeilen-Diff mit Basisversion schreibt (D76); Leitplanken:
keine beschädigte Notation, kein [^] von einem Agenten. Geteiltes Dokument
und Git zugleich: nach jedem Agenten-Zug committet der Server über
pull-doc --git-commit (D88). Entscheidungen der Multiple-Choice-Runde in
RFC §11 und D93.
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):
- #trk.link: Link an existing ticket to a node (S) :#trk.resolve.read
Ein Knopf im Knoten-Fenster bindet ein bestehendes Taiga-Ticket an den
Knoten: Die eingefügte Ticket-URL trägt Slug, Typ und Nummer selbst; eine
nackte Nummer wird per Probe Story-dann-Task aufgelöst. Verknüpft wird erst
nach Anzeige des Betreffs; geschrieben werden Ref-Token und ggf.
&taiga.<slug> in einem Undo-Schritt, der geholte Stand füllt den
Ticket-Cache. Eine URL einer fremden Instanz wird benannt statt verknüpft.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Beim Deploy der stabilen Instanz gehen diese Knoten live; SPEC §4
unterscheidet [x] fertig von [^] in Produktion (D30):
- #trk.bulk: Diverging tickets show up in the diagram (M) :#trk.resolve.read
- #trk.bulk.proxy: One request fans out into by_ref queries server-side
- #trk.bulk.mark: A diverging ref turns amber on its node
Beim Deploy der stabilen Instanz gehen diese Knoten live; SPEC §4
unterscheidet [x] fertig von [^] in Produktion (D30):
- #trk.create.task: A dialog-free "create task" under a story ancestor (S) :#trk.create.tasks,#trk.resolve.read
Beim Deploy der stabilen Instanz gehen diese Knoten live; SPEC §4
unterscheidet [x] fertig von [^] in Produktion (D30):
- #trk.write: Write the status back (M) :#trk.resolve
Weicht der Ticket-Status von der Statusbox ab, markiert das Knoten-Fenster
die Abweichung und bietet beide Richtungen ausdrücklich an.
- „nach Taiga schreiben": Spalte des Projekts suchen (Taiga schreibt nach Id,
die Namen sind je Projekt frei) und mit der zuletzt GELESENEN `version`
patchen — hat jemand dazwischen geändert, lehnt Taiga ab und der Text steht
im Fenster, statt dass etwas überschrieben wird.
- „aus Taiga übernehmen": `setStatusBox()` schreibt die Box in die Textzeile,
undo-fähig wie jede andere Änderung.
- Schreibbar sind nur die fünf abgebildeten Zustände; `[?]`, `[!]`, `[-]` und
der neutrale Knoten lassen das Ticket unangetastet — mit Begründung im
Fenster.
- Proxy: zwei Spaltenlisten (`/taiga/{userstory,task}-statuses?slug=`) und
zwei Schreib-Endpunkte (`PATCH …/{ref}/status?slug=`); die Zielspalte wählt
der Editor, das Backend parst die Notation nicht (D14).
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):
- #trk.resolve.read: Read title, link and status
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>
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
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
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>
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>
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>
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
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>
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
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>
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
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>
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>
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>
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>
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>
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>
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
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>
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>
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.
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
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.
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
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>