Deck-opslag is niet transactioneel: de .md staat al op schijf als een sidecar daarna faalt #1949

Closed
opened 2026-09-03 10:14:19 +00:00 by brenno · 0 comments
Owner

Het probleem

Opslaan van een project schrijft eerst afbeeldingen en de .md (atoomair), en daarna pas de sidecars: annotaties (.ink.json), gebruikersnotities, MIAUW-dispositie, zegel, weigeringen.

Faalt een sidecar — schijf vol, rechten, onderbroken schrijven — dan gooit saveDeckDetailed en het tabblad blijft vuil. Op schijf staat intussen al de nieuwe markdown. Bij heropenen of crashherstel uit het bestand: de .md is nieuw, de sidecars zijn oud of weg. Annotaties, notities of een zegel lijken verdwenen terwijl de gebruiker dacht dat opslaan was afgebroken.

Hetzelfde patroon geldt voor het kopiëren van afbeeldingen naar images/: dat gebeurt met gewone File.copy vóór de atomaire .md. Een crash middenin laat een halve images/-map achter.

Hoe het nu werkt

lib/services/file/file_service_project.dart, _writeProject:

final markdown = _md.generateDeck(updatedDeck);
await writeStringAtomic(File(filePath), markdown);
updatedDeck = DocumentIntegrity.recordWrittenBytes(updatedDeck, markdown);
await _writeSidecar(updatedDeck, filePath);
await _writeUserNotesSidecar(updatedDeck, filePath);
await _writeMiauwSidecar(updatedDeck, filePath);
await _writeSealSidecar(updatedDeck, filePath);
await _writeDismissalsSidecar(updatedDeck, filePath);

De .md is bewust atoomair (writeStringAtomic). De sidecars en asset-kopieën zijn dat als keten niet: er is geen rollback als stap 2 van 6 faalt.

atomic_file_test.dart en save_hardening_test.dart dekken losse schrijffouten, niet "sidecar faalt ná geslaagde .md".

Waarom dat zo gebouwd is

De .md moet pure Marp blijven; specifieke data hoort ernaast (FILE_FORMAT). Elke sidecar is later toegevoegd. Atoomair schrijven per bestand was de lat; de keten als één transactie is nooit afgedwongen.

Denkrichting (niet uitgewerkt)

Mogelijkheden, oplopend:

  1. Sidecars naar een temp-map schrijven en pas daarna de .md atoomair vervangen (of andersom: sidecars eerst, .md als laatste commit-punt), met opruimen van wezen bij falen.
  2. Bij een sidecar-fout de vorige .md terugzetten vanuit de atomic-backup als die er nog is.
  3. Het tabblad vuil en een melding die zegt wat wél op schijf staat ("presentatie bewaard, notities niet").

Geen formaatwijziging. De sidecars blijven sidecars.

Raakvlakken

  • lib/services/file/file_service_project.dart (_writeProject, _copyLogoToProject, image-copy)
  • lib/state/deck_provider.dart (_saveToPath)
  • tests: test/atomic_file_test.dart, test/save_hardening_test.dart
## Het probleem Opslaan van een project schrijft eerst afbeeldingen en de `.md` (atoomair), en daarna pas de sidecars: annotaties (`.ink.json`), gebruikersnotities, MIAUW-dispositie, zegel, weigeringen. Faalt een sidecar — schijf vol, rechten, onderbroken schrijven — dan gooit `saveDeckDetailed` en het tabblad blijft vuil. Op schijf staat intussen al de nieuwe markdown. Bij heropenen of crashherstel uit het bestand: de `.md` is nieuw, de sidecars zijn oud of weg. Annotaties, notities of een zegel lijken verdwenen terwijl de gebruiker dacht dat opslaan was afgebroken. Hetzelfde patroon geldt voor het kopiëren van afbeeldingen naar `images/`: dat gebeurt met gewone `File.copy` *vóór* de atomaire `.md`. Een crash middenin laat een halve `images/`-map achter. ## Hoe het nu werkt `lib/services/file/file_service_project.dart`, `_writeProject`: ```dart final markdown = _md.generateDeck(updatedDeck); await writeStringAtomic(File(filePath), markdown); updatedDeck = DocumentIntegrity.recordWrittenBytes(updatedDeck, markdown); await _writeSidecar(updatedDeck, filePath); await _writeUserNotesSidecar(updatedDeck, filePath); await _writeMiauwSidecar(updatedDeck, filePath); await _writeSealSidecar(updatedDeck, filePath); await _writeDismissalsSidecar(updatedDeck, filePath); ``` De `.md` is bewust atoomair (`writeStringAtomic`). De sidecars en asset-kopieën zijn dat als keten niet: er is geen rollback als stap 2 van 6 faalt. `atomic_file_test.dart` en `save_hardening_test.dart` dekken losse schrijffouten, niet "sidecar faalt ná geslaagde `.md`". ## Waarom dat zo gebouwd is De `.md` moet pure Marp blijven; specifieke data hoort ernaast (FILE_FORMAT). Elke sidecar is later toegevoegd. Atoomair schrijven per bestand was de lat; de *keten* als één transactie is nooit afgedwongen. ## Denkrichting (niet uitgewerkt) Mogelijkheden, oplopend: 1. Sidecars naar een temp-map schrijven en pas daarna de `.md` atoomair vervangen (of andersom: sidecars eerst, `.md` als laatste commit-punt), met opruimen van wezen bij falen. 2. Bij een sidecar-fout de vorige `.md` terugzetten vanuit de atomic-backup als die er nog is. 3. Het tabblad vuil *en* een melding die zegt wat wél op schijf staat ("presentatie bewaard, notities niet"). Geen formaatwijziging. De sidecars blijven sidecars. ## Raakvlakken - `lib/services/file/file_service_project.dart` (`_writeProject`, `_copyLogoToProject`, image-copy) - `lib/state/deck_provider.dart` (`_saveToPath`) - tests: `test/atomic_file_test.dart`, `test/save_hardening_test.dart`
brenno 2026-09-03 14:27:00 +00:00
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
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
LibreKAT/Ocideck#1949
No description provided.