- 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]]↗
Chat Room State Survives Statedump and Restore
Claim
In the eOS-Harness MVA running against DGD 1.7.9 plus the eOS-kernellib chat-app persistence branch carrying the kernel-layer cohesive lift (Vault + Schema + XML + Index + Merry + property-change dispatcher), a chat session established on in-memory property-bearing objects survives a dump_state() plus restart cycle with no serialization code, no schema registration, and no external database. The dispatch shape is: a boot-time test driver clones a fresh room, has two users join and exchange three messages via chat::post_message, sets one user's chat-user.mention-tracker to a cross-clone reference to the other user's object, saves the room and both accounts as non-static object globals, schedules a verify call_out, and dumps a snapshot via /usr/System/sys/persist_helper::trigger_dump_and_exit before exiting. An external restart against the snapshot resumes the dumped image and fires the surviving call_out, which observes that all three messages, both member accounts (live clones with their chat-user.name values intact), and the cross-clone mention-tracker reference all survived -- the mention-tracker resolving to the same user object, not a copy -- and that a third user joining the restored room observes the full prior session. The application code does not opt into persistence; the runtime image is the durable store.
Grounds
This is an Empirical Observation. The grounds are a captured two-boot smoke run 2026-05-29 against the live MVA demonstration instance with examples/chat-app/ (deployed from eOSContinuum/eOS-kernellib) overlaid into the isolated runtime staging tree.
Setup (verifiable from repo state):
- Driver: DGD 1.7.9 built from eOSContinuum/dgd (binary symlinked into the runtime staging tree).
- Kernel layer: eOSContinuum/eOS-kernellib's chat-app persistence-phases branch at HEAD
e2c2126(built atop kernel-layer baselineecb4e58). - Application layer:
examples/chat-app/(this commit's deliverable), deployed into the runtime staging tree'susr/Chatpath. - Runtime config: the instance config at the eOS-Harness repo root, telnet
127.0.0.1:8023, binary/HTTP127.0.0.1:8080, isolated runtime staging tree layout.
Load-bearing source (the persistence phases in examples/chat-app/sys/test.c):
/* phase 10: PERSIST SETUP */
persist_room = spawn_room("ChatApp:Room:PersistD");
persist_alice = alice;
persist_bob = bob;
CHAT_DAEMON->join_room(alice, persist_room);
CHAT_DAEMON->join_room(bob, persist_room);
CHAT_DAEMON->post_message(alice, persist_room, "hi bob");
CHAT_DAEMON->post_message(bob, persist_room, "hey alice");
CHAT_DAEMON->post_message(alice, persist_room, "@bob ping");
/* a cross-clone semantic reference: alice's mention-tracker points AT
* bob's user object. */
alice->set_property("chat-user.mention-tracker", ({ bob }));
call_out("persist_verify", 3);
log_line("ChatApp:test: PERSIST-SETUP OK");
PERSIST_HELPER->trigger_dump_and_exit();
/* phase 11: PERSIST VERIFY -- fires from the surviving call_out */
mention = persist_alice->query_mention_tracker();
if (sizeof(mention) != 1 || mention[0] != persist_bob) {
log_line("ChatApp:test: FAIL: PERSIST-VERIFY mention-tracker cross-clone ref lost");
return;
}
carol = spawn_user("carol-restored");
CHAT_DAEMON->join_room(carol, persist_room);
/* ... asserts 3 messages, now-3-member roster ... */
log_line("ChatApp:test: PERSIST-VERIFY OK");
The persist_room, persist_alice, and persist_bob variables are declared as non-static object globals on the test driver so DGD's state dump captures them. The persist_verify call_out is scheduled at t=3 so it is in the snapshot but has not yet fired at dump time; after restore its scheduled time has elapsed and DGD fires it as soon as the system is back up (the same pattern as examples/merry-app/sys/test.c's persistence phases).
Boot sequence:
$ rm -rf usr/Chat # (in the isolated runtime staging tree)
$ rm -f state/snapshot* state/swap
$ <runtime-provisioning script>
$ cp -R examples/chat-app usr/Chat
# Boot 1 (cold): setup phases run; the persistence phase dumps a snapshot and exits.
$ dgd <instance config>
** DGD 1.7.9
** Initializing...
** Initialization complete.
** admin: actor bob not authorized for kick in room ChatApp:Room:LobbyA [caught]
** System halted.
# Boot 2 (restore): the verify phase fires from the surviving call_out.
$ dgd <instance config> state/snapshot &
** DGD 1.7.9
** State restored.
** System halted. on boot 1 confirms the dump_state(FALSE) plus shutdown() path ran cleanly; ** State restored. on boot 2 confirms DGD resumed the dumped image rather than cold-booting.
Smoke transcript (the persistence portion of the result log after both boots):
ChatApp:test: starting
ChatApp:test: CAP-REJECT OK
ChatApp:test: CAP-ACCEPT OK
ChatApp:test: PERSIST-SETUP OK
ChatApp:test: PERSIST-VERIFY OK
PERSIST-VERIFY OK writes to the sentinel file only when, after the restore:
persist_roomresolves to a non-nil object (the saved global survived the snapshot).persist_room->query_messages()returns 3 entries (the message log survived).persist_room->query_members()returns 2 entries (the membership survived).persist_alice->query_chat_name()is"alice"andpersist_bob->query_chat_name()is"bob"(the user accounts survived as live clones with their names).persist_alice->query_mention_tracker()[0] == persist_bob(the cross-clone object reference resurrected as the same object, not a copy).- a freshly-cloned carol joins the restored room, is admitted, sees 3 messages, and brings the roster to 3.
All assertions ran inside catch{} blocks; any failure writes a distinct FAIL sentinel instead of PERSIST-VERIFY OK.
Process identity check: two distinct DGD processes are involved by design -- boot 1 (cold) writes the snapshot and exits; boot 2 (restore) is a separate process started against that snapshot. The point of the Observation is precisely that state crosses the process boundary via the snapshot, with no application-level serialization. The boot-2 process emitted ** State restored. (not ** Initialization complete.), confirming it resumed the dumped image.
What the evidence establishes:
- A chat session built on in-memory property-bearing clones survives a snapshot plus restart with zero application persistence code -- no
save()calls, no schema, no marshaling, no database. - Object references survive as references: alice's mention-tracker pointed at bob's user object before the dump and resolves to the same bob object after restore (
mention[0] == persist_bob), demonstrating DGD's object-reference resurrection across the snapshot boundary. - Scheduled
call_outs survive the snapshot and fire after restore once their scheduled time has elapsed -- the mechanism by which the post-restore verification runs at all. - A user with no prior involvement in the session (carol, cloned fresh after restore) sees the full restored state, confirming the restored room is fully live, not a degraded read-only artifact.
What the evidence does not establish:
- Durability beyond the image snapshot. The room state rides the snapshot; it is not written to an on-disk structured store. A from-scratch cold boot without the snapshot loses the in-memory session entirely -- this is the paired negative Observation's subject. On-disk durability (surviving a lost snapshot or a migration to another host) is the Schema/Marshal path that
examples/vault-app/demonstrates; the chat-app room is not Schema-registered. - Crash-consistency. The snapshot was taken by a clean
dump_state()plusshutdown(), not by recovery from an abrupt process kill. The Conviction's "snapshot reflects committed state up to the snapshot moment" stance is exercised for the clean-dump case only; crash-recovery semantics are a separate claim. - Large-state scaling. The session is three messages and three clones. The snapshot mechanism's cost as state grows (dump time, snapshot size, restore time) is not measured here.
- Concurrent mutation during dump. The dump fires from a
call_outafter the setup stack unwinds, so no mutation is in flight during the snapshot. Behavior under concurrent writes at dump time is not exercised.
What Would Revise It
- A statedump taken while a write is in flight. A probe that triggers
dump_state()from inside an in-progresspost_message(rather than after the stack unwinds) and observes whether the snapshot captures a consistent or torn message log would extend the claim from "clean-dump persistence" to "persistence under concurrent mutation". - A crash-recovery probe. Killing the DGD process with
SIGKILLmid-session (no clean dump) then restarting against the last periodic snapshot, observing how much committed state is recovered, would document the crash-consistency boundary the clean-dump case leaves open. - A large-session scaling probe. A session with thousands of messages and hundreds of member clones, measuring dump time and snapshot size, would establish whether the no-bookkeeping persistence story holds at scale or develops a measurable snapshot-cost cliff.
- A cross-clone reference cycle. The mention-tracker reference is acyclic (alice -> bob). A probe establishing a reference cycle (alice -> bob -> alice via mutual mention-trackers) and verifying both resurrect correctly would extend the object-reference-resurrection claim to cyclic graphs.
- A DGD or eOS-kernellib upgrade that changes snapshot semantics. The current evidence rests on DGD 1.7.9 plus the cited eOS-kernellib HEAD. A future runtime release that changes
dump_state()behavior, object-reference resurrection, orcall_out-queue persistence would invalidate the snapshot claim; the pinning is part of the grounds.
Sources
examples/chat-app/sys/test.c(eOS-kernellib) -- the persistence-setup and persistence-verify phases are the load-bearing test driver code. The setup phase establishes the session and dumps; the verify phase fires from the survivingcall_outand asserts survival.examples/chat-app/obj/room.c(eOS-kernellib) -- the Room clonable. Property-storage backed via/lib/util/properties;query_messagesandquery_membersexpose the persisted state.query_state_root()returns"ChatApp:Room"but the clonable is NOT Schema-registered, so its state is in-memory-only (rides the snapshot, not the disk).examples/chat-app/obj/user.c(eOS-kernellib) -- the User clonable.query_mention_trackerexposes the cross-clone reference list;set_property("chat-user.mention-tracker", ...)writes it.examples/chat-app/sys/chat.c(eOS-kernellib) --post_messageappends tochat-room.message-log;join_roomadds the member and subscription.src/usr/System/sys/persist_helper.c(eOS-kernellib) -- the System-tier helper whosetrigger_dump_and_exitschedulesdump_state(FALSE)plusshutdown()on acall_outso the caller's stack unwinds before the snapshot is taken.examples/merry-app/sys/test.c(eOS-kernellib) -- the prior two-boot persistence pattern this Observation's phases mirror; the merry-app walkthrough lists "LPC global variables" among the orthogonal-persistence guarantees its cycle exercises.docs/chat-applications.md(eOS-kernellib) -- the walkthrough; the "Persistence" section narrates the setup phase, the verify phase, and the negative case.examples/chat-app/README.md(eOS-kernellib) -- the three-boot verify recipe.- The result log written into the runtime staging tree's
usr/Chat/data/path -- carriesPERSIST-SETUP OKandPERSIST-VERIFY OKafter the two-boot smoke. - The instance config -- runtime config (telnet 8023, binary/HTTP 8080, isolated source directory, snapshot dump file).
- The runtime-provisioning script -- the rsync-into-isolated-runtime layout used to materialize the staging tree's source directory.
Relations
-
conforms_to::[[Observation Form Contract]]
- Carries
has_epistemic_status::[[Empirical Observation]], names the measurement (one two-boot smoke run with six post-restore assertions in the verify phase), states the limits of the measurement (clean-dump only; image-snapshot durability not on-disk durability; acyclic reference; small state), and lists concrete revision conditions.
- Carries
-
informs_downstream::[[Runtime State Is Persistent by Default, Not by Application Discipline]]
- The persistence Conviction's stance is that the runtime image is the persistence layer and application code does not opt into persistence. This Observation is the positive evidence: a chat session survives a snapshot cycle with zero application persistence code. The Conviction's "What It Asks" warns against save-on-mutation patterns and external state stores; this Observation demonstrates the application getting persistence for free from the platform, exactly the no-bookkeeping experience the Conviction defends.
-
grounded_in::[[Conviction Form Contract]]
- The persistence demonstration inherits the same "runtime, not application discipline" stance the Conviction names. No
save()call appears anywhere in the chat application's persistence path; the durability is the platform's standing condition.
- The persistence demonstration inherits the same "runtime, not application discipline" stance the Conviction names. No
-
contrasts_with::[[Chat Room State Held Only In Memory Does Not Survive a Cold Boot Without a Snapshot]]
- The negative-case peer Observation. The two together mark the boundary of orthogonal persistence: snapshot-and-restore preserves the entire in-image session (this Observation), but a cold boot WITHOUT the snapshot finds an empty world (the negative). Reading one without the other gives an incomplete picture: this case alone could be misread as "in-memory state is durable, full stop"; the negative case fixes the boundary at "durable across restore, not across a from-scratch boot -- on-disk Vault is what survives that".