Samenwerken: een gewijzigde paneelzoom reist niet mee, en niets bewaakt die richting #1803
Labels
No labels
accepted
bug
declined
docs
duplicate
enhancement
good first issue
in-progress
needs-info
privacy
security
triage
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set
Reference
LibreKAT/Ocideck#1803
Loading…
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Wat er misgaat
Werken twee mensen samen aan één deck, dan reist een gewijzigde paneelzoom
niet mee. De een sleept de zoom van een
bulletsImage- oftwoImages-afbeeldingnaar 140%, de ander blijft 0% zien. Er komt geen melding, geen conflict, geen
spoor — de twee schermen lopen stil uiteen en blijven zo.
De asymmetrie maakt het extra verwarrend: een nieuwe dia draagt de zoom wél
mee (die gaat als hele dia over de lijn), en een hersynchronisatie herstelt
hem ook. Alleen het wijzigen van de zoom op een dia die beide kanten al hebben,
komt niet aan. Wie het probeert te reproduceren door een verse dia te maken, ziet
dus niets.
Reproductie
bulletsImage-dia met een afbeelding en synchroniseert.bijsnijdingen, afhankelijk van wie het laatst schreef.
Waar het zit
imageZoomstaat niet inSlideField(lib/collab/deck_op.dart), terwijlzijn eigen buren er wél in staan:
imageSize,imageFocalX,imageFocalY,imageFocalX2,imageFocalY2.collab_deck_diff.dartloopt bij een bewerkingop een bestaande dia letterlijk over
SlideField.values, dus wat daar niet instaat wordt niet gediffed en er is geen terugval op een hele-dia-operatie.
collab_codec.dartdraagtimageZoomwél (regels 202 en 279), en dát is precieswaarom insert en snapshot het goed doen en de bewerking niet.
Dit is geen bewuste grens maar een vergeten veld, en de datums laten dat zien:
f62d4abe(30-07-2026) zette het getypte operatiemodel neer, mét defocal-velden en
imageSize;5f926d28(12-08-2026) voegdeimageZoomtoe aan het model, twee weken later;imageZoomheeft nooit indeck_op.dartgestaan.Waarom geen enkele test dit ving
Er staan twee pariteitscontroles, en ze kijken allebei dezelfde kant op:
test/deck_op_test.dart:236— elkeSlideFieldheeft een testgeval;test/collab_codec_test.dart:379— de codec beeldt elkeSlideFieldaf.Beide bewaken "alles wat ín
SlideFieldstaat wordt afgehandeld". Geen van beidebewaakt de andere richting: "elk synchroniseerbaar veld van
Slidestaat inSlideField". Een veld toevoegen aanSlidehaalt dus geen enkele poortomlaag, en dat is exact hoe dit erdoorheen kwam.
De bredere waarneming — en waarom een losse reparatie niet genoeg is
imageZoomis niet de enige sleutel die de hele-dia-codec wel kent enSlideFieldniet. Er zijn er zestien meer:tableRows,bulletMarkerOverride,improvementLayout,viewLimit,renderPage,privacy,quality,contentRedacted,mediaRedacted,aiAssistedFields,findingRole,timelineLayout,timelineReveal,timelineAnimationMs,timelineCurrentIndex(en
id, dat terecht geen veldbewerking is).Een deel daarvan is met opzet buiten de synchroniseerbare oppervlakte
gelaten — de doc-comment bovenaan
collab_deck_diff.dartnoemt tabelrijen,annotaties en het zegel bij name. Maar aan de code is niet te zien wélke van de
zestien bewust zijn en welke vergeten. Dat onderscheid bestaat alleen in iemands
hoofd, en dát is het eigenlijke defect: de volgende keer gaat het op precies
dezelfde manier mis.
Voorstel
imageZoomtoevoegen aanSlideField, aanslideFieldValueen aan deapply-kant, met een regressietest die eerst rood staat: twee cliënten, zoom
gewijzigd op een bestaande dia, waarde komt aan.
Slidestaatin
SlideField, óf op een expliciete uitsluitingslijst met een reden erbij.Dan is "bewust niet gesynchroniseerd" een geschreven keuze in plaats van een
afwezigheid, en faalt een nieuw veld de poort tot iemand kiest.
welke van de twee het is. Dat is het echte werk; punt 1 is een regel.
Waarom dit nu boven kwam
Gevonden tijdens het ontwerp voor #1801 (beeldverwijzingen). Een callout wijst
naar een plek in de afbeelding, en die plek wordt uitgerekend over de bijsnijding
— dus zoom en focal. Lopen twee mensen uiteen op de zoom, dan wijst dezelfde
callout bij hen naar iets anders. Dit is daar slice 1 van, maar het staat op
zichzelf: het gaat vandaag al mis, zonder dat callouts bestaan.
Opgepakt. Tak:
fix/1803-collab-imagezoom-pariteit.Verwachte reikwijdte:
lib/collab/deck_op.dart(het veld plusslideFieldValueen de apply-kant), een pariteitspoort in de ontbrekende richting met een expliciete, beredeneerde uitsluitingslijst, en tests intest/deck_op_test.dart/test/collab_deck_diff_test.dart. Begin met de regressietest die eerst rood staat.Opgelost en gemerged: PR #1805.
Punt 1 — het veld.
imageZoomstaat nu inSlideField,slideFieldValue,de apply-kant en de codec-soortenkaart. De regressietest stond eerst rood tegen
de onherstelde code, met precies de goede reden:
Expected: an object with length of <1>, Actual: []— de diff maakte er nul ops van.Die test leest bewust de wáárde ná toepassing en niet of een hervergelijking leeg
is. De bestaande
expectReproduces-helper, in het bestand omschreven als "thestrongest check", is voor deze klasse fout blind: een veld dat de diff niet kent
is in béíde richtingen onzichtbaar, dus de hervergelijking komt leeg terug
terwijl er niets is overgekomen.
Punt 2 — de poort.
make check-collab-field-paritystelt nu de vraag dieniemand stelde. Elk veld van
Slidestaat inSlideField, óf op eenuitsluitingslijst mét de reden ernaast, óf op een schuldlijst die alleen mag
krimpen. Getoetst in twee richtingen: stil op de echte repo, luid op een geplante
overtreding. De schuldlijst rijdt mee in
make ratchets, zodat stilstandzichtbaar wordt.
Punt 3 — de classificatie, deels. Vijf velden staan nu als bewuste
uitsluiting vastgelegd, elk met de reden:
id(de ops sleutelen erop),mediaRedacted,contentRedactedenrenderPage(voorbijgaandeprojectie-/renderstand, zo benoemd in
slideToJson) entableRows(eenvastgelegde v1-grens uit de doc-comment van
collab_deck_diff.dart).De overige achttien staan als schuld, niet als besluit. Dat is met opzet: ik
heb alleen geclassificeerd waar geschreven bewijs voor was. Of een tijdlijninstelling
of een privacydispositie hoort mee te reizen is een keuze, geen constatering, en
die verzin ik hier niet — de poort zorgt er nu voor dat het een keuze blíjft in
plaats van een gat.
Wat er onderweg groter bleek. Zeven van die achttien zitten helemáál niet in
de samenwerklaag:
anchor,nextAnchor,ganttScale,ganttSections,menuLayout,tableColumnAlignments,tableNumberColumns. Niet inslideToJson, niet inslideFromJson— nagekeken, in beide richtingen nultreffers. Een codec-rondgang verliest ze, dus ook een nieuwe dia komt bij de
ander aan met die velden op hun standaardwaarde. Dat breekt een vastgelegde
belofte: de doc-comment van
slideToJsonzegt "Every field is carried so thereceiver reproduces the slide exactly (P3)".
Dat is een andere reparatie met eigen tests en zit bewust niet in deze PR. Het
verdient een eigen issue; die leg ik apart voor.
Poorten:
make checkvolledig groen (dekking 87,1%, per-bestandsvloer nuleronder),
check-secretsensastschoon, beide CI-poorten groen.Vervolg vastgelegd. De poort uit deze PR bracht de hele oppervlakte in kaart; wat daar uit kwam staat nu als losse issues in plaats van als schuldlijst-zonder-eigenaar:
slideToJson-belofte "Every field is carried … (P3)")expectReproducesheet "the strongest check" maar is blind voor precies deze klasse foutDe schuldlijst
unsyncedBaselineblijft de teller: hij mag alleen krimpen, enmake ratchetslaat stilstand zien.