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):

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:

  1. persist_room resolves to a non-nil object (the saved global survived the snapshot).
  2. persist_room->query_messages() returns 3 entries (the message log survived).
  3. persist_room->query_members() returns 2 entries (the membership survived).
  4. persist_alice->query_chat_name() is "alice" and persist_bob->query_chat_name() is "bob" (the user accounts survived as live clones with their names).
  5. persist_alice->query_mention_tracker()[0] == persist_bob (the cross-clone object reference resurrected as the same object, not a copy).
  6. 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:

What the evidence does not establish:

What Would Revise It

Sources

Relations