Skip to content

M4-16 Sync engine: run documents

No Date Ruling Reason
D-M4-16-0 2026-10-15 Base int/m1-wave24 at 2a72cca, branch feat/M4-16-sync-run-documents in its own worktree; the branch is not merged locally and not pushed, the PR lane lands it. The brief relies on no core rule or ruling that main has and the base lacks (R-SY-06 is in the German chapter 04 of the base), so nothing is ported. The base has no section 10 in this file, so the rows stand in a section of the ticket. Text this ticket adds is English. The brief's 15-client-architecture.md, 04-sync-protocol.md and 09-agents.md are 15-client-architektur.md, 04-sync-protokoll.md and 09-agenten.md on the base. Instructions of the launcher, which override the brief's landing step; merged names win over the brief (D-W-100, D-W-114).
D-M4-16-1 2026-10-15 The store depends on no transport and no api_client type: whole documents come through the port RunDocumentFetcher.fetchRun(RunId) and frames through FrameTicker.requestFrame(callback); run.updated payloads come in as decoded maps. The ticker is a request per dirty period instead of a stream of ticks, so an idle store asks for no frame. Goal and Design note 1 of the brief; M4-17 wires the ports to the dispatcher of M4-14, getRun and the scheduler of the app.
D-M4-16-2 2026-10-15 An invalid patch (an op other than add, replace, remove, a missing target or parent, a missing value, a malformed pointer, the root pointer, a path through a non-container, ops that is not a list of objects, a frame with ops but without base_version) is a gap: the store refetches the whole document and never applies a part. applyRunPatch copies the root and the touched branches only and leaves its input unchanged. Design note 2 of the brief; a partial state is a state the server never had (04 11.3 item 4).
D-M4-16-3 2026-10-15 Coalescing per frame: every change marks its run dirty and requests one frame; at the frame each listener of a dirty run gets the newest document (null when the run is not focused, ended or has no document) unless it has seen exactly that state. A new listener gets the current state at the next frame, not synchronously. Design note 1 and 15 7.4: the interface draws at most once per frame.
D-M4-16-4 2026-10-15 During a refetch of a run its frames with ops wait in a queue; when the fetch returns, the fetched document replaces the local one only when its version is newer, and the queued frames run through the rule again in arrival order (older ones drop, one that still does not fit refetches again). A snapshot applies at once also during a fetch. A failed fetch keeps the last good document and drops the queue, so an always failing fetch cannot loop; the next frame or focus retries. Each fetch takes a new generation number; a result counts only while its run still waits for that number, so unfocus, onRunEnded or a newer focus make it stale. The frame's version is written into the document after a patch or a snapshot. Design note 1 of the brief.
D-M4-16-5 2026-10-15 RunDocument reads stall_warned_at from the raw map and gives null without it; the API schema Run of this base does not name the field (requirement 10 of the brief; the report recommends the optional field). A step without ordinal sorts after those with one in document order, a step whose parent is missing is a top-level step, and steps on a parent cycle appear at the top level, so every step appears exactly once. Requirement 10 and Design note 3 of the brief; the server assigns ordinal, a step is never lost from the view.
D-M4-16-6 2026-10-17 The ticket was ported from its integration base onto main with only its own commit; main's sections stay and this section joins them, the integration rows of the base are not carried over. Port onto main (PR lane).