feat(collab): snapshot-herbaselijning op een niet-nul versie (§5.2, #1005) #1008

Merged
brenno merged 5 commits from collab/snapshot-rebaseline into main 2026-07-31 08:06:10 +00:00
Owner

Closes #1005.

Vervolg op #1004 binnen Fase 0.5 (COLLABORATION.md §5.2). Een deelnemer die
bijkomt begon voorheen vanaf de basislijn bij versie 0 en speelde de héle op-log
opnieuw af — bij een lange sessie werd bijkomen daardoor traag. Nu start een
nieuwe deelnemer (en een terugkerende eigenaar) vanaf de laatste snapshot en
leest alleen de recente records. Raakt het .md niet (P2): alles in de sidecar.

Ontwerp

  • De snapshot draagt zijn log-positie. CollabSnapshot is {version, seq, slides}; seq is het hoogste sequencenummer dat de basislijn al omvat. Een
    joiner start zijn CollabSession op initialVersion = version en zijn
    transport op initialSeq = seq, dus hij haalt alleen de records ná de basislijn
    op. (Een pre-§5.2-snapshot zonder seq leest als 0.) Ook een terugkerende
    eigenaar (§5.3) gebruikt dit.
  • De autoriteit herbaselijnt periodiek. In de HandoverCoordinator schrijft
    de zittende autoriteit na elke rebaseEveryOps versies een verse
    CollabSnapshot.capture(deck, version, transport.lastSeq). Getriggerd op een
    ops-teller, geen wandklok — deterministisch toetsbaar. Elke autoriteit doet dit
    (de sidecar-snapshot is niet de saveDeck die een tijdelijke autoriteit moet
    laten).
  • writeSnapshot overschrijft, dus er is altijd precies één (de nieuwste)
    basislijn; oude op-records blijven staan, zodat de join-race onschuldig is.

Bewuste keuzes / uitgesteld

  • Log-compactie (de oude records écht wissen om de sidecar-omvang te
    begrenzen) is een apart vervolg: #1007. Het vraagt een WebDAV-DELETE (een
    nieuwe, data-muterende uitgaande operatie → security-review) en strand-recovery.
    Deze increment lost de join-snelheid op; de op-log blijft groeien tot #1007.
  • Locks worden niet meegedragen. De snapshot draagt alleen de slide-staat, dus
    een joiner reconstrueert locks alleen uit events ná de basislijn. Aanvaardbaar:
    locks zijn adviserend (de autoriteit serialiseert elke edit) — gedocumenteerd in
    §5.2.

Reikwijdte

collab_snapshot.dart (seq), collab_session.dart (initialVersion),
webdav_async_transport.dart (initialSeq/lastSeq), collab_session_launch.dart
(joiner + host-resume vanaf de basislijn), handover_coordinator.dart
(herbaselijning). Geen nieuwe l10n (onzichtbaar voor de gebruiker), geen
afhankelijkheid.

Toetsing

Na een implementatie-correctheid-review en een bewaker-review; elke bevinding
verwerkt (de eigenaar-hervatting vanaf een niet-nul snapshot is nu getoetst met
version ≠ seq; de herbaselijn-test onderscheidt lastSeq van version via een
lock-record; de changelog maakt het size/speed-onderscheid net zo scherp als de
docs). Nieuwe tests o.a.: snapshot-seq round-trip + default, sessie-startversie,
een joiner die aantoonbaar alleen de recente records leest (read-tellende store),
herbaselijn-drempel, en de eigenaar-hervatting. make check groen (87,3%),
make check-secrets en make sast schoon. Geen goldens (geen rendering).

🤖 Generated with Claude Code

Closes #1005. Vervolg op #1004 binnen Fase 0.5 (COLLABORATION.md §5.2). Een deelnemer die bijkomt begon voorheen vanaf de basislijn bij versie 0 en speelde de héle op-log opnieuw af — bij een lange sessie werd bijkomen daardoor traag. Nu start een nieuwe deelnemer (en een terugkerende eigenaar) vanaf de laatste snapshot en leest alleen de recente records. Raakt het `.md` niet (P2): alles in de sidecar. ## Ontwerp - **De snapshot draagt zijn log-positie.** `CollabSnapshot` is `{version, seq, slides}`; `seq` is het hoogste sequencenummer dat de basislijn al omvat. Een joiner start zijn `CollabSession` op `initialVersion = version` en zijn transport op `initialSeq = seq`, dus hij haalt alleen de records ná de basislijn op. (Een pre-§5.2-snapshot zonder `seq` leest als `0`.) Ook een terugkerende eigenaar (§5.3) gebruikt dit. - **De autoriteit herbaselijnt periodiek.** In de `HandoverCoordinator` schrijft de zittende autoriteit na elke `rebaseEveryOps` versies een verse `CollabSnapshot.capture(deck, version, transport.lastSeq)`. Getriggerd op een ops-teller, geen wandklok — deterministisch toetsbaar. Elke autoriteit doet dit (de sidecar-snapshot is niet de `saveDeck` die een tijdelijke autoriteit moet laten). - **`writeSnapshot` overschrijft**, dus er is altijd precies één (de nieuwste) basislijn; oude op-records blijven **staan**, zodat de join-race onschuldig is. ## Bewuste keuzes / uitgesteld - **Log-compactie** (de oude records écht wissen om de sidecar-*omvang* te begrenzen) is een apart vervolg: **#1007**. Het vraagt een WebDAV-DELETE (een nieuwe, data-muterende uitgaande operatie → security-review) en strand-recovery. Deze increment lost de join-*snelheid* op; de op-log blijft groeien tot #1007. - **Locks worden niet meegedragen.** De snapshot draagt alleen de slide-staat, dus een joiner reconstrueert locks alleen uit events ná de basislijn. Aanvaardbaar: locks zijn adviserend (de autoriteit serialiseert elke edit) — gedocumenteerd in §5.2. ## Reikwijdte `collab_snapshot.dart` (`seq`), `collab_session.dart` (`initialVersion`), `webdav_async_transport.dart` (`initialSeq`/`lastSeq`), `collab_session_launch.dart` (joiner + host-resume vanaf de basislijn), `handover_coordinator.dart` (herbaselijning). Geen nieuwe l10n (onzichtbaar voor de gebruiker), geen afhankelijkheid. ## Toetsing Na een implementatie-correctheid-review en een bewaker-review; elke bevinding verwerkt (de eigenaar-hervatting vanaf een niet-nul snapshot is nu getoetst met `version ≠ seq`; de herbaselijn-test onderscheidt `lastSeq` van `version` via een lock-record; de changelog maakt het size/speed-onderscheid net zo scherp als de docs). Nieuwe tests o.a.: snapshot-seq round-trip + default, sessie-startversie, een joiner die aantoonbaar alleen de recente records leest (read-tellende store), herbaselijn-drempel, en de eigenaar-hervatting. `make check` groen (87,3%), `make check-secrets` en `make sast` schoon. Geen goldens (geen rendering). 🤖 Generated with [Claude Code](https://claude.com/claude-code)
CollabSnapshot is now {version, seq, slides}: seq is the highest log sequence the
baseline subsumes, so a joiner can resume just above it. A missing seq decodes as
0 (a pre-§5.2 baseline at version 0).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
CollabSession starts _version at initialVersion (a joiner from a re-baselined
snapshot begins at a non-zero version). WebdavAsyncTransport starts _lastSeq/
_knownMaxSeq at initialSeq (so a joiner fetches only records posted after the
baseline) and exposes lastSeq (which the authority stamps into a re-baseline).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Both joinCollabSession and hostCollabSession's resume path read the latest
snapshot's version and seq and start their session and transport there, so a late
joiner (and a returning owner) fetches only the records posted since — never the
whole op log. A read-counting store proves the old records are skipped.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The HandoverCoordinator, being the authority's periodic driver, publishes a fresh
CollabSnapshot.capture(deck, version, transport.lastSeq) after every rebaseEveryOps
op versions, so a later joiner starts from it. Ops-triggered, no wall-clock. Any
authority does it — writing the sidecar snapshot is not the saveDeck a temporary
authority must avoid.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
docs(collab): document §5.2 re-baselining and the deferred compaction (#1005)
All checks were successful
scans / scans (pull_request) Successful in 3m34s
465d8a177b
COLLABORATION.md §5.2 gains the Fase 0.5 realisation (snapshot seq, joiner/owner
resume, periodic re-baselining), the note that locks are not carried (advisory,
self-correcting), and the deferral of log compaction to #1007 (needs a WebDAV
DELETE + strand-recovery). This fixes join *speed*; the op-log still grows until
compaction. SOURCE_MAP and CHANGELOG updated to match.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
brenno merged commit 3b2db0d2a4 into main 2026-07-31 08:06:10 +00:00
Sign in to join this conversation.
No description provided.