jdt_move_type: Änderungen werden nicht auf Disk geschrieben #79

Closed
opened 2026-04-10 07:11:03 +00:00 by hauschel.fred · 2 comments
Collaborator

Problem

jdt_move_type meldet SUCCESS, aber die Änderungen landen nicht auf dem Filesystem. Das Refactoring findet nur im In-Memory-Buffer statt.

Reproduktion

jdt_move_type({
  typeName: "de.g4ch.cg.activitypub.vocab.VocabAs",
  targetPackage: "de.g4ch.cg.shared.vocab",
  updateReferences: true,
  preview: false
})

Rückgabe: {"status": "SUCCESS", "newLocation": "de.g4ch.cg.shared.vocab.VocabAs"}

Tatsächliches Ergebnis auf Disk:

  • Quelldatei existiert noch, Package-Deklaration unverändert
  • Zieldatei wurde NICHT erstellt
  • Nur 1 von 85 Import-Updates wurde auf Disk geschrieben

Ursache

Change.perform() schreibt im Headless-Mode in den ITextFileBufferManager In-Memory-Buffer. Ohne Eclipse UI fehlt der automatische Save.

Bisheriger Fix-Versuch (Problem 3)

In RefactoringSupport.performChange() wurde ein expliziter Flush implementiert:

  1. Betroffene IFiles aus dem Change-Tree sammeln
  2. ICompilationUnit.hasUnsavedChanges() prüfen
  3. cu.save() aufrufen
  4. refreshLocal() auf betroffene Projekte

Dieser Fix greift nicht zuverlässig. Mögliche Ursachen:

  • Der Flush-Codepfad wird nicht für alle CUs erreicht
  • Die Quelldatei (Move-Source) wird von einem anderen Codepfad behandelt als die Import-Updates
  • hasUnsavedChanges() liefert false obwohl der Buffer dirty ist

Nächster Schritt

Log-Output in RefactoringSupport.performChange() hinzufügen, um zu prüfen:

  • Welche CUs werden als "unsaved" erkannt?
  • Wird der Flush-Codepfad überhaupt erreicht?
  • Was passiert mit der Quelldatei (die verschoben werden soll)?

Kontext

Reproduziert in zwei unabhängigen Sessions (ActorId, VocabAs) mit identischem Verhalten — jeweils genau 1 von ~85 Dateien auf Disk aktualisiert. Siehe agent_communication.md (Problem 3 + Problem 5).

## Problem `jdt_move_type` meldet `SUCCESS`, aber die Änderungen landen nicht auf dem Filesystem. Das Refactoring findet nur im In-Memory-Buffer statt. ## Reproduktion ``` jdt_move_type({ typeName: "de.g4ch.cg.activitypub.vocab.VocabAs", targetPackage: "de.g4ch.cg.shared.vocab", updateReferences: true, preview: false }) ``` **Rückgabe:** `{"status": "SUCCESS", "newLocation": "de.g4ch.cg.shared.vocab.VocabAs"}` **Tatsächliches Ergebnis auf Disk:** - Quelldatei existiert noch, Package-Deklaration unverändert - Zieldatei wurde NICHT erstellt - Nur 1 von 85 Import-Updates wurde auf Disk geschrieben ## Ursache `Change.perform()` schreibt im Headless-Mode in den `ITextFileBufferManager` In-Memory-Buffer. Ohne Eclipse UI fehlt der automatische Save. ## Bisheriger Fix-Versuch (Problem 3) In `RefactoringSupport.performChange()` wurde ein expliziter Flush implementiert: 1. Betroffene `IFile`s aus dem Change-Tree sammeln 2. `ICompilationUnit.hasUnsavedChanges()` prüfen 3. `cu.save()` aufrufen 4. `refreshLocal()` auf betroffene Projekte **Dieser Fix greift nicht zuverlässig.** Mögliche Ursachen: - Der Flush-Codepfad wird nicht für alle CUs erreicht - Die Quelldatei (Move-Source) wird von einem anderen Codepfad behandelt als die Import-Updates - `hasUnsavedChanges()` liefert `false` obwohl der Buffer dirty ist ## Nächster Schritt Log-Output in `RefactoringSupport.performChange()` hinzufügen, um zu prüfen: - Welche CUs werden als "unsaved" erkannt? - Wird der Flush-Codepfad überhaupt erreicht? - Was passiert mit der Quelldatei (die verschoben werden soll)? ## Kontext Reproduziert in zwei unabhängigen Sessions (ActorId, VocabAs) mit identischem Verhalten — jeweils genau 1 von ~85 Dateien auf Disk aktualisiert. Siehe `agent_communication.md` (Problem 3 + Problem 5).
Author
Collaborator

Umbau-Versuch auf Branch fix/79-move-type-cross-module

Ein weiterer Fix-Versuch liegt isoliert auf Branch fix/79-move-type-cross-module (gepusht). Er kompiliert sauber (mvn clean compile), die Disk-Schreib-Ursache ist aber noch nicht verifiziert.

Was der Branch ändert

  • move_type: MoveDescriptor → interne JavaMoveProcessor-API (voller Workspace-Scope für Referenzsuche); neuer targetProject-Param für Cross-Modul-Moves; Heartbeat gegen MCP-Timeout bei großen Moves.
  • RefactoringSupport.performChange(): globaler WORKSPACE_MUTATION_LOCK, Headless-AssertionFailedException-Handling, Reflection-basierte Fallbacks (manueller File-Move + manueller TextEdit-Apply) für MoveCompilationUnitChange.
  • Alle Refactorings laufen jetzt über performChange; MANIFEST.MF + org.eclipse.core.filebuffers.

Status / offen

⚠️ Noch nicht mergen. Ob jetzt tatsächlich alle ~85 Dateien auf Disk landen, ist ungeprüft. Nächster Schritt: End-to-End-Verifikation gegen tests/fixtures/fixture-external — Quelle gelöscht, Zieldatei erstellt, alle Import-Updates auf Disk. Erst grün → PR nach main.

Die eigenständigen Nebenfixes dieser Session (mcpServers-Wrapper, OSGi-Version, Throwable-Catch, self-healing Nav-Errors + Index-Probe, Maven-Dep-Rewire, Launcher-Recovery) sind bereits auf main.

## Umbau-Versuch auf Branch `fix/79-move-type-cross-module` Ein weiterer Fix-Versuch liegt isoliert auf Branch `fix/79-move-type-cross-module` (gepusht). Er **kompiliert sauber** (`mvn clean compile`), die Disk-Schreib-Ursache ist aber **noch nicht verifiziert**. ### Was der Branch ändert - `move_type`: `MoveDescriptor` → interne `JavaMoveProcessor`-API (voller Workspace-Scope für Referenzsuche); neuer `targetProject`-Param für Cross-Modul-Moves; Heartbeat gegen MCP-Timeout bei großen Moves. - `RefactoringSupport.performChange()`: globaler `WORKSPACE_MUTATION_LOCK`, Headless-`AssertionFailedException`-Handling, Reflection-basierte Fallbacks (manueller File-Move + manueller `TextEdit`-Apply) für `MoveCompilationUnitChange`. - Alle Refactorings laufen jetzt über `performChange`; `MANIFEST.MF` + `org.eclipse.core.filebuffers`. ### Status / offen ⚠️ **Noch nicht mergen.** Ob jetzt tatsächlich alle ~85 Dateien auf Disk landen, ist ungeprüft. Nächster Schritt: End-to-End-Verifikation gegen `tests/fixtures/fixture-external` — Quelle gelöscht, Zieldatei erstellt, alle Import-Updates auf Disk. Erst grün → PR nach `main`. Die eigenständigen Nebenfixes dieser Session (mcpServers-Wrapper, OSGi-Version, Throwable-Catch, self-healing Nav-Errors + Index-Probe, Maven-Dep-Rewire, Launcher-Recovery) sind bereits auf `main`.
Author
Collaborator

Debugging-Historie aus agent_communication.md (Datei wird gelöscht, Stand 2026-04-10)

Vier zusammenwirkende Headless-Bugs, iterativ mit der Platform-Session verifiziert (Cross-Modul-Move VocabAscg-shared-kernel, 85 Referenzen):

Bug A — TextFileChange schreibt nicht auf Disk. TextFileChange.releaseDocument() wirft AssertionFailedException vor dem Save-Code. FORCE_SAVE + IWorkspace.run() löst das für die meisten TextFileChanges; für die Move-Quelldatei nicht, dort ist der Buffer nach dem Assert korrupt.

Bug B — MoveCompilationUnitChange crasht headless. doPerformReorg()CompilationUnit.move() aktiviert org.eclipse.jdt.uiClassNotFoundException. Fix: manueller Move als Fallback (Quelldatei von Disk lesen, nicht über CU-Buffer; Package-Deklaration anpassen; Ziel via IPackageFragment.createCompilationUnit(); Quelle löschen). Verifiziert (Fixtures, Version ≥ 202604101145).

Bug C — Cross-Modul-Ziel im falschen Projekt. moveType() suchte das Target-Package nur im Source-Root des Quell-Typs; findPackageInSourceProject() reichte nicht. Fix: optionaler targetProject-Parameter, ohne ihn bei Mehrdeutigkeit hilfreicher Fehler statt stillem Fehlplatzieren. Verifiziert durch Platform-Session (Version 202604101200): Quelle gelöscht, Ziel im richtigen Projekt.

Bug D — Import-Updates: 1 von 85 auf Disk (offen). Nach der AssertionFailedException ist der ITextFileBuffer disconnected: getTextFileBuffer() liefert null oder isDirty() ist false, daher greift buffer.commit() nicht. Sackgasse: Pre-Connect via bufferManager.connect() vor perform() macht es schlimmer, bricht die Referenzzählung, danach wirft jede Datei den Assert. Letzter Ansatz (Version 202604101442): bei Assert das TextEdit per Reflection aus dem Change holen (Feld fEdit), Datei von Disk lesen, edit.copy().apply(doc), via IFile.setContents() zurückschreiben. In Fixtures grün (intra-Modul, 3 Dateien), gegen den 85-Dateien-Fall nicht mehr verifiziert. Genau dieser Stand liegt auf fix/79-move-type-cross-module (siehe Kommentar oben).

Offene Frage für die Verifikation: Ob der Reflection-Pfad wirklich für alle betroffenen TextFileChanges erreicht wird, oder ob der Assert nur bei einem Teil feuert und der Rest still im Buffer bleibt.

## Debugging-Historie aus `agent_communication.md` (Datei wird gelöscht, Stand 2026-04-10) Vier zusammenwirkende Headless-Bugs, iterativ mit der Platform-Session verifiziert (Cross-Modul-Move `VocabAs` → `cg-shared-kernel`, 85 Referenzen): **Bug A — `TextFileChange` schreibt nicht auf Disk.** `TextFileChange.releaseDocument()` wirft `AssertionFailedException` *vor* dem Save-Code. `FORCE_SAVE` + `IWorkspace.run()` löst das für die meisten TextFileChanges; für die Move-Quelldatei nicht, dort ist der Buffer nach dem Assert korrupt. **Bug B — `MoveCompilationUnitChange` crasht headless.** `doPerformReorg()` → `CompilationUnit.move()` aktiviert `org.eclipse.jdt.ui` → `ClassNotFoundException`. Fix: manueller Move als Fallback (Quelldatei von Disk lesen, nicht über CU-Buffer; Package-Deklaration anpassen; Ziel via `IPackageFragment.createCompilationUnit()`; Quelle löschen). **Verifiziert** (Fixtures, Version ≥ 202604101145). **Bug C — Cross-Modul-Ziel im falschen Projekt.** `moveType()` suchte das Target-Package nur im Source-Root des Quell-Typs; `findPackageInSourceProject()` reichte nicht. Fix: optionaler `targetProject`-Parameter, ohne ihn bei Mehrdeutigkeit hilfreicher Fehler statt stillem Fehlplatzieren. **Verifiziert** durch Platform-Session (Version 202604101200): Quelle gelöscht, Ziel im richtigen Projekt. **Bug D — Import-Updates: 1 von 85 auf Disk (offen).** Nach der `AssertionFailedException` ist der `ITextFileBuffer` disconnected: `getTextFileBuffer()` liefert `null` oder `isDirty()` ist `false`, daher greift `buffer.commit()` nicht. Sackgasse: Pre-Connect via `bufferManager.connect()` vor `perform()` macht es schlimmer, bricht die Referenzzählung, danach wirft *jede* Datei den Assert. Letzter Ansatz (Version 202604101442): bei Assert das `TextEdit` per Reflection aus dem Change holen (Feld `fEdit`), Datei von Disk lesen, `edit.copy().apply(doc)`, via `IFile.setContents()` zurückschreiben. In Fixtures grün (intra-Modul, 3 Dateien), gegen den 85-Dateien-Fall **nicht mehr verifiziert**. Genau dieser Stand liegt auf `fix/79-move-type-cross-module` (siehe Kommentar oben). Offene Frage für die Verifikation: Ob der Reflection-Pfad wirklich für alle betroffenen `TextFileChange`s erreicht wird, oder ob der Assert nur bei einem Teil feuert und der Rest still im Buffer bleibt.
Commenting is not possible because the repository is archived.
No project
No assignees
1 participant
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
ai-tools/jdt-mcp-server#79
No description provided.