ADR-042: Extract NodeRecord into vox-populi-types (L2)
ADR-042: Extract NodeRecord into vox-populi-types (L2)
Section titled “ADR-042: Extract NodeRecord into vox-populi-types (L2)”Status: Accepted — implemented 2026-05-24.
Supersedes: D-7 (workspace audit 2026-05-23) — original suggestion to move to L0 was wrong.
Context
Section titled “Context”NodeRecord is a 28-field pure-data struct that describes a compute node in
the Populi mesh: GPU/CPU capabilities, security keys (Ed25519), scheduling
metadata, model advertisements, and donation policy.
It was originally defined in vox-populi/src/node_registry.rs alongside the
LocalRegistry persistence layer (the two were entangled). The workspace audit
flagged the entanglement as a D-series concern (topology type in a L3 crate).
The original D-7 proposal was to move NodeRecord to vox-mesh-types (L0) to
allow vox-orchestrator and other mid-layer crates to depend on it without
pulling in all of vox-populi. However, NodeRecord contains:
pub capabilities: vox_repository::TaskCapabilityHints,vox-repository is an L2 crate (repo catalog, host probing). An L0 crate
cannot depend on an L2 crate; the layer check would fail with a C2/C3 violation.
Decision
Section titled “Decision”Create a new crate vox-populi-types at L2, sitting alongside
vox-repository (both L2, no fan-in between them).
Layer placement:
L0: vox-mesh-types (pure mesh protocol — no repo deps)L2: vox-repository (host probing, repo catalog)L2: vox-populi-types (NodeRecord + task capability hints composition) ← newL3: vox-populi (file I/O, local registry, mesh client)L4: vox-plugin-populi-mesh (dispatch, API handlers)vox-populi-types depends on:
vox-repository(forTaskCapabilityHints)vox-mesh-types(forModelAdvertisement,WorkerDonationPolicy)serde,serde_json(serialization)
vox-populi-types does NOT depend on:
vox-db,tokio,anyhow, or any async/DB cratevox-orchestratoror any L3+ crate
Consequences
Section titled “Consequences”Positive:
vox-orchestrator(L3) can importNodeRecorddirectly fromvox-populi-types(L2) rather than the fullvox-populi(L3) crate. This avoids a same-layer circular dependency risk and keeps types accessible to the routing engine.vox-plugin-populi-meshcan depend onvox-populi-typesdirectly, removing a redundant path through the fullvox-populicrate for type-only imports.- The LoC budget in
vox-populiis reduced by extracting the pure-data struct.
Negative / risks:
- One more crate in the workspace (+1 to the total count).
vox-populistill re-exportsNodeRecordfromvox-populi-typesfor backwards compatibility; callers can migrate at their own pace.
Rejected Alternatives
Section titled “Rejected Alternatives”| Alternative | Reason rejected |
|---|---|
Move NodeRecord to vox-mesh-types (L0) | Violates layering: TaskCapabilityHints is L2 |
Keep NodeRecord in vox-populi | No semantic issue, but forces L3 dependency for type-only consumers |
Inline TaskCapabilityHints fields into NodeRecord | Breaks SSOT; would duplicate host-probe logic |
Create vox-populi-types at L0 using only L0-compatible capability fields | Lossy — VRAM, arch, labels, routing tier are not in vox-mesh-types |