fix(git): laat de deckmap heel bij openen, opslaan en samenvoegen (#670) #676

Merged
brenno merged 3 commits from fix/native-merge-deckmap-670 into main 2026-07-22 19:51:16 +00:00
Owner

Sluit #670.

Een deckmap is meer dan deck.md. De cijfers van een gekoppelde grafiek staan in data/*.json ernaast, en sinds #541 de notities in deck.user-notes.json.

De oorzaak lag een stap eerder dan het issue vermoedde. Het native openpad riep withRepoSidecars helemaal niet aan — alleen het REST-pad deed dat. Er stond dus een deck in de editor dat zijn eigen lagen niet kende, en omdat élke schrijfweg de deckmap vervangt door wat de app samenstelde, ruimde de eerstvolgende gewone opslag ze op. Geen botsing nodig, geen merge, geen melding — en het deck zag er heel uit: de source-verwijzing stond er nog, alleen de cijfers waren weg.

Drie plekken, want het ging op drie manieren mis:

  1. openen (tabs_provider_git_native.dart) hydrateert nu met withRepoSidecars, net als het REST-pad. readDeck gaf de bestanden hierboven toch al mee, dus het kost geen tweede leesronde;
  2. samenvoegen (resolveRepoDeckMerge) hydrateert elke kant met álle lagen in plaats van alleen de notities — anders draagt merge.merged geen inline cijfers, levert chartDataFilesOf niets, en verdwijnt data/*.json ook bij een geslaagde merge, precies zoals je in je tweede lezing schreef;
  3. de terugvalweg houdt onze hele kant vast, niet alleen deck.md. Git's eigen tekst-merge is dan al over de deckmap gegaan, dus de sidecars daar kunnen conflictmarkeringen dragen; die worden teruggeschreven zoals ze bij ons stonden.

De contractvraag, en waarom hij toch mee kon

mergeRemote beloofde dat wat de resolver niet noemt verwijderd mag worden. Die volledigheid kón de resolver niet waarmaken: hij krijgt drie deck.md-blobs en een lezer per kant, en weet verder niets van de map. Het contract is omgedraaid naar bijwerken — files wordt geschreven, deletes wordt verwijderd, de rest blijft staan. Dat is jouw tweede voorstel, en het laat de twee vlakken hetzelfde zeggen: het REST-vlak werkte al met commitFiles(upserts:, deletes:).

commitDeck blijft de map wél vervangen. Daar is de app werkelijk de autoriteit over wat een deck bevat — zo verdwijnt het databestand van een verwijderde dia — en buildDeckRepoFiles levert elk lid. De asymmetrie is het punt: vervangen mag precies wanneer de schrijver de hele set kent, en de merge-resolver kent die niet. Dat staat zo in GIT_STORAGE §9.7.

Regressietest

git_native_merge_test.dart, drie gevallen zoals je vroeg plus één die je nog niet kon weten:

  • de gewone opslag (geen merge) — dit was het pad dat als eerste omviel;
  • de geslaagde merge;
  • de terugvalweg, met een deck.md van hen die de importpoort weigert. Die toetst tegen de clone en niet tegen de origin: bij clean: false wordt er niets gepusht, dus de origin bewijst daar niets. Mijn eerste versie deed dat wél en stond daardoor onterecht groen — de enige van de drie die niet vanzelf rood werd.
  • plus de notitie op het gewone-opslagpad: de bestaande notitietests dekken alleen de merge, en die hydrateerde al.

Alle drie de nieuwe gevallen stonden rood tegen de onherstelde code.

Een refactor die erbij hoorde

De reparatie duwde TabsNotifier over zijn klasseplafond (2352 / 2325). Niet de basislijn verhoogd maar gedrag eruit gehaald, en wel het gedrag dat er sowieso te vaak in stond: de "mem: uit de webstore, anders van schijf"-regel stond vijf keer letterlijk uitgeschreven, één per schrijfpad, en drie openpaden meldden op drie plekken hetzelfde over onleesbare grafiekdata. Nu _repoAssetBytes en _reportMissingChartData, top-level in dezelfde library. De klasse zakt naar 2316 regels en de basislijn gaat mee omlaag.

Poort

make check groen (niet door tail gepijpt). Documentatie bijgewerkt: CHANGELOG, SOURCE_MAP, GIT_STORAGE §9.2 en §9.7 — die beschreven allemaal nog het oude contract, en §9.2 zei "deck.md and sidecars" over een openpad dat op het native vlak geen sidecars las.

Geen afhankelijkheid erbij (geen SBOM), geen nieuwe zichtbare tekst (geen l10n). make check-secrets en make sast niet gedraaid.

Sluit #670. Een deckmap is meer dan `deck.md`. De cijfers van een gekoppelde grafiek staan in `data/*.json` ernaast, en sinds #541 de notities in `deck.user-notes.json`. **De oorzaak lag een stap eerder dan het issue vermoedde.** Het native openpad riep `withRepoSidecars` helemaal niet aan — alleen het REST-pad deed dat. Er stond dus een deck in de editor dat zijn eigen lagen niet kende, en omdat élke schrijfweg de deckmap *vervangt* door wat de app samenstelde, ruimde de **eerstvolgende gewone opslag** ze op. Geen botsing nodig, geen merge, geen melding — en het deck zag er heel uit: de `source`-verwijzing stond er nog, alleen de cijfers waren weg. Drie plekken, want het ging op drie manieren mis: 1. **openen** (`tabs_provider_git_native.dart`) hydrateert nu met `withRepoSidecars`, net als het REST-pad. `readDeck` gaf de bestanden hierboven toch al mee, dus het kost geen tweede leesronde; 2. **samenvoegen** (`resolveRepoDeckMerge`) hydrateert elke kant met álle lagen in plaats van alleen de notities — anders draagt `merge.merged` geen inline cijfers, levert `chartDataFilesOf` niets, en verdwijnt `data/*.json` ook bij een *geslaagde* merge, precies zoals je in je tweede lezing schreef; 3. **de terugvalweg** houdt onze hele kant vast, niet alleen `deck.md`. Git's eigen tekst-merge is dan al over de deckmap gegaan, dus de sidecars daar kunnen conflictmarkeringen dragen; die worden teruggeschreven zoals ze bij ons stonden. ## De contractvraag, en waarom hij toch mee kon `mergeRemote` beloofde dat wat de resolver niet noemt verwijderd mag worden. Die volledigheid kón de resolver niet waarmaken: hij krijgt drie `deck.md`-blobs en een lezer per kant, en weet verder niets van de map. Het contract is omgedraaid naar bijwerken — `files` wordt geschreven, `deletes` wordt verwijderd, de rest blijft staan. Dat is jouw tweede voorstel, en het laat de twee vlakken hetzelfde zeggen: het REST-vlak werkte al met `commitFiles(upserts:, deletes:)`. `commitDeck` blijft de map wél vervangen. Daar is de app werkelijk de autoriteit over wat een deck bevat — zo verdwijnt het databestand van een verwijderde dia — en `buildDeckRepoFiles` levert elk lid. De asymmetrie is het punt: vervangen mag precies wanneer de schrijver de hele set kent, en de merge-resolver kent die niet. Dat staat zo in GIT_STORAGE §9.7. ## Regressietest `git_native_merge_test.dart`, drie gevallen zoals je vroeg plus één die je nog niet kon weten: - de **gewone opslag** (geen merge) — dit was het pad dat als eerste omviel; - de **geslaagde merge**; - de **terugvalweg**, met een `deck.md` van hen die de importpoort weigert. Die toetst tegen de clone en niet tegen de origin: bij `clean: false` wordt er niets gepusht, dus de origin bewijst daar niets. Mijn eerste versie deed dat wél en stond daardoor onterecht groen — de enige van de drie die niet vanzelf rood werd. - plus de **notitie op het gewone-opslagpad**: de bestaande notitietests dekken alleen de merge, en die hydrateerde al. Alle drie de nieuwe gevallen stonden rood tegen de onherstelde code. ## Een refactor die erbij hoorde De reparatie duwde `TabsNotifier` over zijn klasseplafond (2352 / 2325). Niet de basislijn verhoogd maar gedrag eruit gehaald, en wel het gedrag dat er sowieso te vaak in stond: de "`mem:` uit de webstore, anders van schijf"-regel stond **vijf keer letterlijk uitgeschreven**, één per schrijfpad, en drie openpaden meldden op drie plekken hetzelfde over onleesbare grafiekdata. Nu `_repoAssetBytes` en `_reportMissingChartData`, top-level in dezelfde library. De klasse zakt naar 2316 regels en de basislijn gaat mee omlaag. ## Poort `make check` groen (niet door `tail` gepijpt). Documentatie bijgewerkt: CHANGELOG, SOURCE_MAP, GIT_STORAGE §9.2 en §9.7 — die beschreven allemaal nog het oude contract, en §9.2 zei "deck.md and sidecars" over een openpad dat op het native vlak geen sidecars las. Geen afhankelijkheid erbij (geen SBOM), geen nieuwe zichtbare tekst (geen l10n). `make check-secrets` en `make sast` niet gedraaid.
Een deckmap is meer dan `deck.md`. De cijfers van een gekoppelde grafiek
staan in `data/*.json` ernaast, en sinds #541 de notities in
`deck.user-notes.json`. Het native pad las die bestanden bij het openen
niet in, dus stond er een deck in de editor dat ze niet kende — en omdat
elke schrijfweg de deckmap *vervangt* door wat de app samenstelde, ruimde
de eerstvolgende opslag ze op. Geen botsing, geen melding, en het deck zag
er heel uit: de verwijzing bleef staan, alleen de cijfers waren weg.

Drie plekken, want het ging op drie manieren mis:

- **openen** (`tabs_provider_git_native.dart`) hydrateert nu met
  `withRepoSidecars`, net als het REST-pad al deed. `readDeck` gaf de
  bestanden hierboven toch al mee, dus het kost geen tweede leesronde. Dit
  is de oorzaak van de gewone opslag, zonder merge — het kortste pad dat er
  is;
- **samenvoegen** (`resolveRepoDeckMerge`) hydrateert elke kant met álle
  lagen in plaats van alleen de notities. Zonder dat draagt `merge.merged`
  geen inline cijfers, levert `chartDataFilesOf` niets, en verdwijnt
  `data/*.json` ook bij een gelúkte merge;
- **de terugvalweg** houdt onze hele kant vast, niet alleen `deck.md`. Git's
  eigen tekst-merge is dan al over de deckmap gegaan, dus de sidecars kunnen
  conflictmarkeringen dragen; die worden teruggeschreven zoals ze bij ons
  stonden. Eén geweigerde kant hoorde nooit de rest van de map te kosten.

En de eigenlijke fout eronder: `mergeRemote` beloofde dat wat de resolver
niet noemt weg mag. Die volledigheid kón de resolver niet waarmaken — hij
kent de deckmap niet, alleen de drie `deck.md`'s. Het contract is omgedraaid
naar bijwerken: `files` wordt geschreven, `deletes` wordt verwijderd, de rest
blijft staan. Dat is ook hoe het REST-vlak al werkt
(`commitFiles(upserts:, deletes:)`), dus de twee vlakken zeggen nu hetzelfde.

Regressietest voor alle drie de gevallen in `git_native_merge_test.dart`,
plus de notitie op het gewone-opslagpad — de bestaande notitietests dekten
alleen de merge, en die hydrateerde al.

Sluit #670.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
CHANGELOG, SOURCE_MAP en GIT_STORAGE §9.2/§9.7 beschreven het oude
contract — "wat de resolver niet noemt, bestaat na afloop niet meer" —
en §9.2 zei "deck.md and sidecars" over een openpad dat op het native
vlak helemaal geen sidecars las. Beide staan er nu zoals het werkt,
mét waarom de asymmetrie met `commitDeck` geen slordigheid is:
vervangen mag precies wanneer de schrijver de hele set kent, en de
merge-resolver kent die niet.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
refactor(git): één plek voor de bytes van een afbeelding, één voor de melding
Some checks failed
CI / Gate (Linux) · Format · Analyze · Coverage (push) Failing after 28s
CI / Web hardening (push) Failing after 22s
CI / Docs links (push) Failing after 21s
CI / Supply-chain (Trivy · advisory) (push) Failing after 26s
CI / Gate (Linux) · Format · Analyze · Coverage (pull_request) Failing after 26s
CI / Web hardening (pull_request) Failing after 24s
CI / Docs links (pull_request) Failing after 23s
CI / Supply-chain (Trivy · advisory) (pull_request) Failing after 22s
CI / Test (macos-latest) (push) Has been cancelled
CI / Test (windows-latest) (push) Has been cancelled
CI / Test (macos-latest) (pull_request) Has been cancelled
CI / Test (windows-latest) (pull_request) Has been cancelled
2053391b5e
De reparatie van #670 duwde `TabsNotifier` over zijn klasseplafond. Niet
de basislijn verhoogd maar er gedrag uit gehaald, en wel het gedrag dat er
sowieso te vaak in stond:

- `_repoAssetBytes` — de "mem: uit de webstore, anders van schijf"-regel
  stond vijf keer letterlijk uitgeschreven, één per schrijfpad. Vijf
  kopieën van dezelfde regel is er vier te veel: een zesde pad dat er één
  vergeet schrijft een kapotte verwijzing zonder dat er iets misgaat waar
  de app op kan wijzen;
- `_reportMissingChartData` — drie openpaden meldden op drie plekken
  hetzelfde over een grafiek waarvan de cijfers niet te lezen waren.

Beide top-level in dezelfde library, dus buiten de klasse die tegen haar
plafond zit. De klasse zakt van 2352 naar 2316 regels; de basislijn gaat
mee omlaag, want een gewonnen regel die je niet vastzet win je nog een keer.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
brenno merged commit b6f9582521 into main 2026-07-22 19:51:16 +00:00
Sign in to join this conversation.
No description provided.