fix(mesh): publish the estate so every transport can report it - #2309
Merged
Conversation
|
You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard. |
igorls
force-pushed
the
fix/mesh-estate-visible-to-all-clients
branch
from
August 19, 2026 20:49
3fa5707 to
a779002
Compare
The peer sync loop is started only by _serve_http, and the estate it builds (_PEER_SYNC_STATE, _KNOWN_PROFILES) is process memory. But mempalace_mesh_peers ships in every transport, including the stdio servers agents connect through, and those processes never run a sync round. So they answered from a permanently empty estate: every peer reduced to a bare name and url with no reachable, last_success_at, remote_version_vector or profile; origin_profiles holding only this node; and configured peers reported as unnamed_origins -- "known only transitively" -- because their replica_id is learned during a sync and nothing else supplies it. The hub next door had all of it. The sync loop now publishes the estate to mesh_state.json in the per-palace server state directory after every round, alongside the token and serverinfo that already use that directory for exactly this "hub records something other local processes read" purpose. 0600, and written to a temp name then renamed so a reader in another process never observes a half-serialized estate. _mesh_peers_payload merges the published estate underneath any in-process state, per peer, so the process that actually syncs keeps reporting its own fresher observation and every other process reports the hub's instead of nothing. The new estate_source field says where the reading came from: in_process, published_at, and whether the publishing hub is still alive. A crashed hub leaves a last-known-good estate, which is worth showing -- "last seen as" beats a blank node -- but must not be read as live. peers.json tokens are not in the estate and never reach the file; both are asserted.
igorls
force-pushed
the
fix/mesh-estate-visible-to-all-clients
branch
from
August 19, 2026 21:08
a779002 to
3ea3b26
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The bug
_start_peer_sync_thread()is called from exactly one place — inside_serve_http(). The estate that loop builds (_PEER_SYNC_STATE,_KNOWN_PROFILES) is plain process memory. Butmempalace_mesh_peersships in every transport, including the stdio servers agents actually connect through, and those processes never run a sync round.So they answered from a permanently empty estate. Observed from a stdio client against a live three-replica mesh:
No
reachable, nolast_success_at, noremote_version_vector, noprofile— all of which the tool description promises. Both configured peers landed inunnamed_origins("origins known only transitively") because a peer'sreplica_idis learned during a sync and nothing else supplies it. Meanwhile the hub for the same palace had the complete picture.Reproduced against a fresh HTTP server, which shows it is purely about whether a sync round has run in this process:
name,urlname,url,reachable,replica_id,remote_version_vector,profile,last_success_at,last_error,last_error_at,last_pulled_events,last_pulled_artifactsA stdio process never reaches the second row.
The fix
The sync loop publishes the estate to
mesh_state.jsonin the per-palace server state directory — the same place the bearer token andserverinfo.jsonalready live, for the same "the hub records something other local processes read" reason, with the same 0600 and the same pid-liveness trust model. Written to a temp name and renamed, so a reader in another process never observes a half-serialized estate; the file is rewritten every round._mesh_peers_payloadmerges the published estate underneath any in-process state, per peer, so the process that actually syncs keeps reporting its own fresher observation and every other process reports the hub's instead of nothing.Publishing happens once per round rather than per peer — the estate is only coherent after every configured peer has been attempted.
New field
estate_source— additive, nothing renamed or removed (the desktop viewer consumes this payload):A crashed hub leaves a last-known-good estate. That is worth showing — "last seen as" beats a blank node — but must not be read as live, so
writer_alivemarks it rather than the file being deleted on shutdown.Verified end to end
A process with no sync loop, reading an estate a hub published:
Tests
Seven in
TestPublishedEstate(tests/test_logstream_sync.py), all failing ondevelop:peers.jsontokens reach neither the file nor the payload.tmpleft behindwriter_alive: falseFull suite: 4315 passed, 31 skipped. Lint and format clean.