node: make the shared context the sole store of immutable state

Remove the duplicated immutable fields (config, identity, startup_epoch,
started_at, is_leaf_only, max_connections/peers/links) from the Node
struct so the Arc<NodeContext> bundle is the single source of truth.
Previously Node owned these fields and a parallel context copy, kept in
lockstep by rebuild_context() at every mutation site — pure overhead that
existed only because of the duplication.

- Replace rebuild_context() with replace_context(): a clone-edit-swap of
  the whole Arc. The per-instance context stays immutable; mutation swaps
  the Arc. This is the sole runtime mutation path (constructors, leaf_only,
  update_peers).
- Add Copy-returning accessors startup_epoch() and max_connections()/
  max_peers()/max_links(); migrate the remaining direct field readers onto
  the accessors. node_addr()/npub()/Debug now read identity/is_leaf_only
  from the context.
- update_peers reads the pre-update peer set from the live context Arc
  before building a fresh Config + context and swapping — preserving the
  read-before-write ordering its mutation-window test depends on.
- Remove the test-only set_max_* setters; tests set the limits on Config at
  construction instead (new make_node_with_max_peers/links helpers).
- Add a ci-local guard that fails if the Node struct re-declares a bundled
  field, so the single-store invariant can't silently regress.

cargo test --lib 1291/0; clippy -D warnings and release build clean.
This commit is contained in:
Johnathan Corgan
2026-06-02 16:42:05 +00:00
parent 08b8b3908e
commit d9a4a7807c
16 changed files with 208 additions and 230 deletions
+15
View File
@@ -233,6 +233,21 @@ run_build() {
record "clippy" 1
return 1
fi
# Guard: the effectively-immutable state lives solely in NodeContext. The
# Node struct must not re-declare a bundled field (config/identity/
# startup_epoch/started_at/is_leaf_only/node_profile/max_*) — a shadow field
# would silently reopen dual-store divergence between the struct and the
# context. Checks the struct *declaration*, so it is wrap-insensitive.
info "node-context single-store guard"
if awk '/^pub struct Node \{/,/^\}/' src/node/mod.rs \
| grep -qE '^[[:space:]]+(config|identity|startup_epoch|started_at|is_leaf_only|node_profile|max_connections|max_peers|max_links):'; then
fail "Node struct re-declares a bundled immutable field; it must live solely in NodeContext"
record "node-context-guard" 1
return 1
else
record "node-context-guard" 0
fi
}
# ── Stage 2: Unit Tests ───────────────────────────────────────────────────