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>
Der einfache Klick toggelt das Fenster überall — wie der Tipp auf Touch
(D52); der Link eines Knotens ist damit auch am Zeiger der ↗-Knopf im
Fenster, Enter und Strg+Klick öffnen die URL weiterhin direkt. Tastatur-
fokus öffnet nur noch echten (:focus-visible), focusout schließt nicht in
das Fenster hinein, und die Fokus-Rückgabe nach dem Falten ruft das
Fenster ausdrücklich nie.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Eine gemeinsame flex-wrap-Aktions-Zeile für Schreiben/Übernehmen/Öffnen in
der Ticket-Box; mit Taiga-Knöpfen wird das Fenster auf 31rem breiter
(gemessen an den deutschen Labels), umgebrochen wird nur bei echtem
Platzmangel. Der Öffnen-Knopf heißt nur noch «{ref} öffnen» — «in Taiga»
sagen die Nachbarknöpfe.
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.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>
Die offene Frage von #trk.write ist entschieden, gebaut ist nichts: Weicht
der Ticket-Status von der Statusbox ab, wird die Abweichung markiert und mit
zwei ausdrücklichen Aktionen angeboten — nichts geschieht von selbst.
Verworfen sind „der Text gewinnt" (schreibt selbsttätig in ein fremdes,
geteiltes System) und „Taiga gewinnt" (schriebe fortlaufend im Notationstext,
gegen D14); „Zeitstempel entscheidet" ist ohne erfundene Metadaten gar nicht
baubar.
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
Im Text (freistehendes #US-123/#T-1234, D67-Ausschlüsse) wie auf dem
Knoten; das Knoten-Fenster trägt an Refs einen Öffnen-Knopf — der Weg
auf Touch. Adresse aus taigaWeb (/info) + &taiga.slug-Vererbung; in
einem :#…-Token gewinnt weiter der Abhängigkeits-Sprung.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
WERKBAUM_TAIGA_WEB_URL (das Taiga-Frontend, nicht aus der API-URL
ableitbar) kommt als taigaWeb in GET /api/v1/info — ohne Schrägstrich am
Ende, fehlt unkonfiguriert. deploy-backend.sh zieht die Zeile aus der
lokalen .env idempotent in die Server-Umgebung nach.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Der Browser bricht eine zu breit geratene Zeile an jedem Bindestrich —
aus #US-123 wurde #US-/123 über zwei Zeilen. Eine tref-Spanne mit
white-space:nowrap hält das Token zusammen; labelLines() (Export) misst
ihre Textknoten unverändert mit.
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.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>
Die helle Umkehrung des Chips war für die dunkle Tinte-Wurzel gebaut —
seit D39 ist die Wurzel mit Status aber pastellfarben, und Weiß darauf
unsichtbar. Der Chip ist jetzt überall derselbe; desc-mark und nid
bleiben nur auf der statuslosen (dunklen) Wurzel weiß.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Die Ausnahme von der Pfad-Inversion stellte den ganzen Knoten auf volle
Stärke, weil opacity/filter den Ring mitträfen. Jetzt bleibt der Knoten
deckend und die Blässe wird nachgebildet: Papier-Wasch über der Füllung,
gefadete Text-/Rahmenfarbe, Kinder einzeln gedimmt — der Ring steht voll.
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/data/ entsteht beim lokalen bootRun (D77: werkbaum.data-dir) und
war im D85-Commit versehentlich mitgegangen.
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>
1. Neu laden fragt bei URL-Dokumenten nicht mehr (F5 verwirft ohnehin
still, D23); bei Datei-Dokumenten bleibt die Rückfrage. Dabei gefunden
und behoben: Der Knopf erschien auch bei Server-Dokumenten und hätte
die JSON-Antwort der API als Text an alle gepusht.
2. Die Rückfrage beim letzten Dokument sagt, dass danach wieder das
Beispiel dasteht (docDeleteLastConfirm, 9 Sprachen).
3. Ein zweiter Tab, der in dieselbe Ablage schreibt, wird per
storage-Ereignis erkannt und gemeldet (tabConflict, isDocKey in
docstore.js) — kein Sync, aber kein stiller Verlust mehr.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>