Skip to content

feat(node): W1c — composition root, Node exposes 7 seams as Arc<dyn> handles (#1285)#72

Merged
MichaelTaylor3d merged 2 commits into
mainfrom
feat/w1c-composition-root
Jul 21, 2026
Merged

feat(node): W1c — composition root, Node exposes 7 seams as Arc<dyn> handles (#1285)#72
MichaelTaylor3d merged 2 commits into
mainfrom
feat/w1c-composition-root

Conversation

@MichaelTaylor3d

@MichaelTaylor3d MichaelTaylor3d commented Jul 20, 2026

Copy link
Copy Markdown
Contributor

W1c — the #1285 composition-root milestone (Option A, locked by the orchestrator).

  • Node stays ONE concrete struct implementing all 7 seam traits; the composition root exposes each seam as Arc<dyn SeamTrait> via 6 self-referential upcast constructors (as_chain_source/as_peer_network/as_rpc_dispatch/as_content_server/as_capsule_store/as_key_manager) — each Arc::clone(self) as Arc<dyn Trait> (same object, no new alloc). Consumers/FFI/tests hold Arc<dyn SeamTrait> (injectable fakes + per-seam swappable for W2-W5).
  • dig-node-service AppState now holds content_server: Arc<dyn ContentServer>; loopback serve rewired through the seam handle (e2e demonstration).
  • Behaviour-preserving: only the OUTER handle shape becomes Arc<dyn>; openrpc_drift_guard 7/7 green.
  • Folded dig-urn-resolver 0.3.1 -> 0.4.0 (wire-compatible). Added SPEC §13 (seam architecture).
  • MINOR bump: dig-node-core 0.14.0, binary 0.48.0 (additive new API + AppState field, §5.1).

Refs #1285. Inner seam de-tangle (multi-struct decomposition, cross-seam via trait handles, cycles broken by injection) deferred to #1357, sequenced after W2-W5 reshape the 4<->5 / 5<->6 edges.

MichaelTaylor3d and others added 2 commits July 20, 2026 16:29
Co-Authored-By: Claude <noreply@anthropic.com>
Realizes the #1285 W1c milestone with the LOCKED "Option A" shape (main
decider): Node stays ONE concrete struct implementing all 7 seam traits
(unchanged from W1b) — it is NOT split into 7 separate structs. The
composition root exposes each seam as a self-referential Arc<dyn SeamTrait>
upcast of the SAME Arc<Node> (Node::as_chain_source/as_peer_network/
as_rpc_dispatch/as_content_server/as_capsule_store/as_key_manager — no
Arc::new_cyclic, no separate object, so no possibility of the seam-4<->5 /
5<->6 cross-seam cycles the W1c design recon surfaced).

dig-node-service (the single user-facing binary) demonstrates the pattern
end-to-end: AppState now holds content_server: Arc<dyn ContentServer>
alongside node: Arc<Node> — the SAME object, two shapes — and the loopback
plaintext serve path (server::serve_resource/serve_miss) is wired through
the seam handle instead of the concrete Node. This makes the seam boundary
genuinely injectable (a test/FFI caller can hold Arc<dyn ContentServer>) and
lets a later wave (W2-W5) repoint ONE such handle at a different concrete
implementation without touching the other 6 seams or any consumer holding a
different seam's handle.

The INNER cross-seam reaches the W1b carves left in place (self.proxy,
self.p2p_content(), the direct self.anchored_root_resolver field read,
self.maybe_backfill_capsule, RpcDispatch::dispatch calling into every other
seam) are UNCHANGED by this pass, deliberately — de-tangling them into true
per-seam structs is deferred to the follow-up epic #1357, sequenced after
W2-W5 (which reshape several of these exact edges as each seam's concrete
implementation is actually replaced; de-tangling now risks redoing the same
work again).

Folds dig-node-service's dig-urn-resolver dep 0.3.1 -> 0.4.0 (main-confirmed
wire-compatible, byte-identical).

Adds SPEC.md §13 "Internal architecture — the 7 seams": the trait/module
table, the cross-seam-vocabulary-only rule, the Option-A composition-root
shape, the thin-binary invariant, and the #1357 deferred-de-tangle note.

Version: MINOR (dig-node-core 0.13.8->0.14.0, workspace/binary 0.47.8->
0.48.0) — the outer Arc<dyn> handoff adds new public API (the 6 upcast
constructors on Node) and changes dig-node-service's AppState shape
(additive: a new content_server field, still built only via build_state).
No existing public signature was removed or changed; this is a compatible
new-capability bump, never a breaking one.

Green bar: cargo build/test --workspace --locked exit 0, 0 failures across
every suite INCLUDING openrpc_drift_guard (7/7 passed — the objective
invariant proving the served RPC surface didn't drift). cargo clippy
--workspace --all-targets --locked -D warnings clean. cargo fmt --all --
--check clean.

Depends on W1b-6 (merged, #1285). This is the #1285 MILESTONE — on merge
the 7-seam architecture is fully realized: 7 architecturally-separated seam
traits + one thin glue binary holding no seam logic.

Co-Authored-By: Claude <noreply@anthropic.com>
@MichaelTaylor3d
MichaelTaylor3d marked this pull request as ready for review July 21, 2026 01:00

@MichaelTaylor3d MichaelTaylor3d left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

W1c correctness gate (fresh context, effort:high) — PASS on code.

Verified against PR head aaa087c (full diff = 7 files, +135/-10; git diff 3c72d75..aaa087c matches exactly — no hidden changes):

  1. Upcasts are the SAME object, no new alloc — all 6 as_() are Arc::clone(self) as Arc<dyn Trait> (lib.rs), an Arc→Arc unsizing coercion. No Arc::new / Arc::new_cyclic, no state copy. Same object ⇒ same state ⇒ behaviour-preserving.
  2. No behaviour change from the rewire — AppState.content_server = node.as_content_server() (server.rs build_state); the 3 loopback sites (serve_resource, serve_miss ×2) now dispatch through Arc to the SAME Node impl (impl ContentServer for Node, content_serve.rs:267). serve_content_plaintext + manifest_paths are trait methods with identical signatures — dynamic dispatch to the identical code.
  3. openrpc_drift_guard — RPC surface untouched (rpc.rs/meta.rs/dispatch not in diff), so the 7/7 no-drift invariant cannot have drifted; the guard test runs in the CI 'Test + coverage' job.
  4. No new tangle — diff introduces ONLY the 6 additive upcast ctors + the AppState field + 3 rewired sites. No new cross-seam reach; self.proxy/self.p2p_content/self.anchored_root_resolver are unchanged from base = exactly #1357's pre-existing scope.

Also: MINOR bump justified + both files agree (core 0.14.0, service 0.48.0, §5.1-additive: 6 new pub ctors + additive AppState field); dig-urn-resolver 0.3.1→0.4.0 present (wire-compatible); SPEC §13 accurate + references #1357; 0 unresolved review threads.

BEFORE MERGE (non-code, orchestrator): (a) PR title + body are STALE — title still reads '(DESIGN RECON, awaiting decider)' and the body is the recon note; the decision is locked (Option A) and this IS the implementation. The title becomes the squash-commit message, so update it to a clean conventional-commit summary that references #1285/#1357 (body should reference #1357). (b) CI: 'Test + coverage' and the native-package builds were still IN_PROGRESS at review time — merge only once all required checks are green.

@MichaelTaylor3d MichaelTaylor3d changed the title refactor(node): W1c — composition-root split (DESIGN RECON, awaiting decider) feat(node): W1c — composition root, Node exposes 7 seams as Arc<dyn> handles (#1285) Jul 21, 2026
@MichaelTaylor3d
MichaelTaylor3d merged commit b499c6d into main Jul 21, 2026
12 checks passed
@MichaelTaylor3d
MichaelTaylor3d deleted the feat/w1c-composition-root branch July 21, 2026 01:08
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant