- conforms_to::[[Observation Form Contract]]
- has_epistemic_status::[[Empirical Observation]]
- in_practice_domain::[[eOS Continuum]]
- authored_by::[[Christopher Allen]]
- has_lifecycle::[[Seed Stage]]↗
- has_curation::[[Working Draft]]↗
A Lost Update Appears Only Without the Runtime's Serialization of Concurrent Writes
Claim
In the eOS-Harness MVA running against DGD 1.7.9 plus the eOSContinuum/eOS-kernellib feature/cohesive-lift branch, a lost update -- one writer's mutation silently overwritten by another's -- appears only when the writers act on state read before a concurrent commit instead of re-reading at write time. The demonstration is the deliberate contrast to the serialized claim_slot path: obj/room.c carries a claim_slot_stale(user, stale_snapshot) that writes the member list from a snapshot argument (stale_snapshot + ({ user })) rather than re-reading current state. Phase 9b captures one empty member-list snapshot and passes the SAME snapshot to both writers (heidi, ivan). heidi writes empty + ({ heidi }); ivan writes empty + ({ ivan }) -- computed from the same stale empty snapshot, so ivan's write overwrites heidi's, heidi's claim is lost, and only ivan remains (sentinel LOST-UPDATE OK). The lost update is not a property of the runtime; it is a property of acting on a value read before a concurrent commit. The sibling claim_slot avoids it by reading current state at write time, where each writer's read already reflects the other's commit. This bounds the coherent-state mechanism: the runtime's coherent read at write time is what removes the lost update, and the lost update is reproducible only by an application that deliberately caches state across the mutation boundary.
Grounds
This is an Empirical Observation. The grounds are a captured three-boot smoke run 2026-05-30 against the live MVA demonstration instance with examples/chat-app/ overlaid at the isolated runtime staging tree's src/usr/Chat/.
Setup (verifiable from repo state):
- Driver: DGD 1.7.9 built from
dworkin/dgd; binary symlinked at the isolated runtime staging tree'sbin/dgd. - Kernel layer:
eOSContinuum/eOS-kernellibonfeature/cohesive-liftat HEAD2bb1596(the write-coherence chat-app phase). - Application layer:
examples/chat-app/, deployed into the isolated runtime staging tree with the Chat domain overlay. - Runtime config: the instance config; the isolated runtime staging tree layout; the three-boot regression harness, which counts
OKsentinels (expected 13).
Load-bearing source (examples/chat-app/obj/room.c and phase 9b of examples/chat-app/sys/test.c):
/* room.c: the stale-snapshot write -- the contrast to claim_slot */
void claim_slot_stale(object user, object *stale_snapshot)
{
set_property("chat-room.member-list", stale_snapshot + ({ user }));
}
/* test.c phase 9b: both writers act on ONE snapshot captured before either committed */
stale = room_l->query_members(); /* empty, captured once */
room_l->claim_slot_stale(heidi, stale); /* writes empty + heidi */
room_l->claim_slot_stale(ivan, stale); /* writes empty + ivan, overwriting heidi */
/* asserts: heidi NOT a member; ivan a member; sizeof(members) == 1 (the lost update) */
Smoke transcript (the load-bearing portion of the result log after boot 1):
ChatApp:test: COHERENCE-SERIALIZE OK
ChatApp:test: LOST-UPDATE OK
LOST-UPDATE OK writes only when heidi is NOT a member (her claim was lost), ivan IS a member, and the room holds exactly one member -- i.e. the second stale write overwrote the first. The assertion is inside catch{}; heidi surviving (no lost update reproduced), ivan missing, or a wrong member count each log a distinct FAIL.
What the evidence establishes:
- The lost update is reproducible only by caching state across the mutation boundary: both writers computed from a snapshot read before either committed, so the second overwrote the first.
- The runtime's coherent read at write time is what removes it: the sibling
claim_slotpath -- identical except it readsquery_members()at write time rather than taking a snapshot argument -- serializes the same two contenders cleanly (the positive Observation). - The failure mode is an application choice, not a runtime limitation: the platform provides coherent reads at write time; an application that ignores them and acts on a cached snapshot reintroduces the very lost update the primitive removes.
What the evidence does not establish:
- That the runtime ever produces a lost update on its own. The lost update here is forced by
claim_slot_stale, which exists only to make the contrast visible. No path that reads current state at write time produced a lost update. - Cross-instance write loss. The demonstration is single-coherence-domain; write loss across a process or machine boundary (a genuinely concurrent multi-runtime scenario) is outside the platform's primitive and not exercised.
- Optimistic-concurrency or version-check patterns. The negative does not implement compare-and-swap, version stamps, or retry-on-conflict; it shows the bare lost update, which the coherent-read path makes those patterns unnecessary for.
What Would Revise It
- A stale-snapshot write that did NOT lose the first writer's update would contradict the claim that acting on pre-commit state produces the lost update; the current evidence shows heidi's claim overwritten.
- A current-read-at-write-time path that DID produce a lost update would falsify the framing that coherent reads remove it; the sibling
claim_slotpath serializes cleanly. - A DGD or eOS-kernellib change to property-write or atomic-task semantics would invalidate the snapshot; the claim rests on DGD 1.7.9 plus
feature/cohesive-liftHEAD2bb1596.
Sources
examples/chat-app/obj/room.c(ineOS-kernellib feature/cohesive-lift) --claim_slot_stale(the stale-snapshot write) andclaim_slot(the serialized contrast it is paired against).examples/chat-app/sys/test.c-- phase 9b (LOST-UPDATE): both writers act on one snapshot captured before either committed.docs/chat-applications.md(ineOS-kernellib feature/cohesive-lift) -- the "Multi-agent coherence" walkthrough; phase 9b.- The isolated runtime staging tree's
src/usr/Chat/data/test-result.log-- the sentinel file; carriesLOST-UPDATE OKafter a clean boot. - The three-boot regression harness and the instance config.
Relations
-
conforms_to::[[Observation Form Contract]]
- Carries
has_epistemic_status::[[Empirical Observation]], names the measurement (one three-boot smoke run; the phase-9b assertion), states the limits (the lost update is forced byclaim_slot_stalefor contrast; single-coherence-domain; no version-check patterns), and lists concrete revision conditions.
- Carries
-
informs_downstream::[[Multiple Agents See Coherent State Without User-Land Coordination]]
- The Conviction holds that the runtime carries the coherence so agents do not coordinate via locks or eventual-consistency reconciliation. This Observation is the negative evidence: the lost update -- the bug application-layer coordination is normally built to prevent -- appears only when an application acts on state read before a concurrent commit, and is removed by the runtime's coherent read at write time. The drift it guards against is application code that caches state across the mutation boundary and then needs its own coordination to recover.
-
contrasts_with::[[Two Agents Contending for a Bounded Resource: Exactly One Claim Commits]]
- The positive-case peer. That case shows
claim_slotre-reading current state at write time, serializing two contenders so exactly one commits; this case showsclaim_slot_stalewriting from a pre-commit snapshot, producing the lost update. The two together bound the mechanism: coherence holds for reads at write time, and the lost update is reproducible only by deliberately caching state across the mutation boundary.
- The positive-case peer. That case shows