docs/sync-and-op-log/section-conflict-replay.md
Status: Active sync-correctness contract.
This document owns the narrow exception that preserves SECTION reducer semantics when a server rejects a concurrent local operation. The executable owners are:
src/app/op-log/sync/section-conflict-commutativity.util.tssrc/app/op-log/sync/superseded-operation-resolver.service.tssrc/app/op-log/sync/superseded-operation-resolver.service.spec.tse2e/tests/sync/supersync-section-convergence.spec.tsSECTION actions encode ordered relationships across a section and its Project or Tag work context. Replacing a rejected move, removal, or reorder with a snapshot of one entity loses reducer semantics: a task can remain in two containers, disappear from both, or converge with different ordering on each client.
The resolver may therefore replay a rejected SECTION intent instead of collapsing it into a generic entity snapshot. This is a deliberately narrow exception, not permission to replay arbitrary rejected actions.
Only these action families are candidates:
SECTION_UPDATE_ORDERSECTION_ADD_TASKSECTION_REMOVE_TASKReplay is admitted only when all of the following hold:
areCommutingSectionOperations() recognizes the exact pair.The recognized crossings are intentionally limited to:
Missing, ambiguous, malformed, or non-commuting evidence keeps the generic LWW fallback. Never broaden recognition merely because two actions appear harmless in one fixture.
An admitted intent is projected against one stable NgRx snapshot whose state is
fully represented by durable operations. There is no await between the
phantom-change check and snapshot read; the operation-log lock keeps later user
actions behind the recovery transaction.
projectSectionReplayAgainstState() returns one of four outcomes:
Replacement ordering is scoped by section order or work-context task order. Replacements use a merged, incremented clock that dominates the rejected and retained frontiers. The client must not prune that clock before the server performs conflict detection.
The resolver appends all replacement/compensation operations and rejects their stale predecessors in one operation-log transaction. A crash must not expose only one half of the recovery.
Clients in the v18.4.0-v18.4.3 compatibility window understand schema-4 SECTION removals but ignore later work-context anchor fields. A semantic removal is therefore paired with a complete Project/Tag LWW replacement when needed, which their existing reducer can apply to converge task ordering.
Do not use a schema bump as a substitute for this compensation. Any change to
the replacement payload must be checked against the released-fleet rules in
operation-log-architecture.md.
Run the focused unit suite:
npm run test:file src/app/op-log/sync/superseded-operation-resolver.service.spec.ts
Run the real-client convergence scenario through the scheduled SuperSync E2E workflow, or locally when the dedicated server environment is available:
npm run e2e:file e2e/tests/sync/supersync-section-convergence.spec.ts -- --retries=0
The E2E must continue to prove concurrent move, removal, reorder, and dependent placements converge and survive restart.