Skip to content

Lock split topology placement - #1050

Merged
i386 merged 3 commits into
mainfrom
jd/locked-split-topology
Jul 22, 2026
Merged

Lock split topology placement#1050
i386 merged 3 commits into
mainfrom
jd/locked-split-topology

Conversation

@i386

@i386 i386 commented Jul 21, 2026

Copy link
Copy Markdown
Collaborator

Why this exists

The automatic split planner is the right default for normal serving: it chooses participants and layer boundaries from the capacity currently advertised by the mesh. That flexibility is undesirable during controlled lab benchmarks, where discovery timing, peer availability, or a changed VRAM report can otherwise produce a different split between runs.

A topology lock makes placement an explicit benchmark input. The same lock file is copied to every host and pins:

  • the immutable model/package identity and manifest SHA-256;
  • the ordered participating nodes;
  • the exact layer range assigned to each node.

This ensures every host in the lab agrees on the same split topology and makes benchmark results comparable across binaries, branches, runtime settings, and repeated runs. If the requested topology cannot be reproduced exactly, startup fails instead of silently benchmarking a different placement.

How the lock is used

Create a versioned JSON manifest such as:

{
  "version": 1,
  "model": "hf://meshllm/example-layers@immutable-revision",
  "manifest_sha256": "<sha256 of model-package.json>",
  "stages": [
    {
      "node": "micstudio.local",
      "layer_start": 0,
      "layer_end": 31
    },
    {
      "node": "studio54-3.local",
      "layer_start": 31,
      "layer_end": 47
    }
  ]
}

Place the same file on every serving host and launch each node with:

mesh-llm serve \
  --model hf://meshllm/example-layers@immutable-revision \
  --split \
  --split-topology-lock /path/to/topology-lock.json

At startup, the runtime:

  1. verifies that the lock matches the resolved package and manifest digest;
  2. resolves every node selector to exactly one eligible participant;
  3. requires non-empty, contiguous ranges covering the complete model;
  4. runs the normal context, KV-cache, headroom, and VRAM capacity checks against those exact assignments;
  5. starts only if the requested topology can be realized unchanged.

Node selectors may be full iroh endpoint IDs or uniquely advertised hostnames.

Fail-closed behavior

A locked topology is not a preference or planner hint. Membership changes do not replace its stages or collapse it to local serving. If an assigned stage is lost, the route becomes unavailable and is withdrawn after the normal stage-loss grace period.

This prevents a benchmark from continuing under a materially different execution shape while still appearing healthy.

Implementation summary

  • add the hidden --split-topology-lock <path> runtime option;
  • add strict, versioned lock parsing and package/node identity validation;
  • add capacity-aware locked planning to the shared Skippy topology planner;
  • disable replacement replanning and local fallback for locked generations;
  • document the lock format, startup behavior, and runtime verification surface.

Validation

  • cargo fmt --all --check
  • cargo check -p mesh-llm
  • cargo clippy -p skippy-coordinator -p mesh-llm-cli -p mesh-llm-host-runtime -p mesh-llm --all-targets -- -D warnings
  • cargo test -p skippy-coordinator --lib (32 passed)
  • cargo test -p mesh-llm-cli --lib (72 passed)
  • cargo test -p mesh-llm-host-runtime --lib (1703 passed, 8 ignored)
  • just build

Summary by CodeRabbit

  • New Features
    • Added --split-topology-lock <PATH> to pin split node order and layer ranges via a validated JSON lock (requires --split).
    • Split planning now supports locked stage-to-node assignments for deterministic deployments.
    • Locked split loss recovery now withdraws routes after the normal grace period instead of replanning or falling back.
  • Bug Fixes
    • Improved fail-closed validation for locked topology parsing and planning, including clearer failure behavior when the lock can’t be reproduced.
  • Documentation
    • Added/expanded documentation for creating, verifying, and operating locked split topologies, including CLI and design guidance.

@coderabbitai

coderabbitai Bot commented Jul 21, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 5cd8c87e-710b-4d3a-a68f-9f58e6324a5e

📥 Commits

Reviewing files that changed from the base of the PR and between 9160b31 and 1d02631.

📒 Files selected for processing (5)
  • crates/mesh-llm-host-runtime/src/runtime/split_planning.rs
  • crates/skippy-coordinator/src/topology.rs
  • crates/skippy-coordinator/src/topology/locked.rs
  • docs/SKIPPY_SPLITS.md
  • website/src/docs/pages/CLI.md
💤 Files with no reviewable changes (1)
  • crates/skippy-coordinator/src/topology.rs
🚧 Files skipped from review as they are similar to previous changes (4)
  • website/src/docs/pages/CLI.md
  • crates/skippy-coordinator/src/topology/locked.rs
  • docs/SKIPPY_SPLITS.md
  • crates/mesh-llm-host-runtime/src/runtime/split_planning.rs

📝 Walkthrough

Walkthrough

The PR adds a hidden --split-topology-lock option, propagates it through runtime startup, validates and resolves lock files, plans pinned topologies, and prevents locked splits from replanning after stage loss. It also adds recovery tests and operational documentation.

Changes

Locked split topology

Layer / File(s) Summary
CLI and startup wiring
crates/mesh-llm-cli/src/parser.rs, crates/mesh-llm-cli/src/parser/commands.rs, crates/mesh-llm/src/lib.rs, crates/mesh-llm-host-runtime/src/runtime/{options.rs,startup_models.rs,startup_handles.rs,run_auto.rs,serving_surface.rs,local.rs}
Adds the hidden lock-path option, validates its relationship with --split, and propagates it through runtime configuration and startup specifications.
Lock-file loading and participant resolution
crates/mesh-llm-host-runtime/src/runtime/split_topology_lock.rs, crates/mesh-llm-host-runtime/src/runtime/mod.rs
Parses and validates lock metadata, resolves endpoint or normalized hostname selectors, and rejects ambiguous or invalid assignments.
Locked topology planning
crates/skippy-coordinator/src/topology*, crates/mesh-llm-host-runtime/src/runtime/split_planning.rs, crates/mesh-llm-host-runtime/src/runtime/local_split.rs
Validates contiguous stage coverage and pinned nodes, computes a fitting resource-aware plan, and selects locked planning during split startup.
Locked topology recovery
crates/mesh-llm-host-runtime/src/runtime/local_split/{coordinator.rs,recovery.rs,tests.rs,test_support.rs}
Extracts recovery helpers and ensures locked topologies withdraw after stage loss instead of replanning or falling back locally.
Operational documentation
docs/SKIPPY_SPLITS.md, docs/design/TESTING.md, website/src/docs/pages/CLI.md
Documents lock-file requirements, startup usage, verification, and locked-stage loss behavior.

Estimated code review effort: 4 (Complex) | ~60 minutes

Sequence Diagram(s)

sequenceDiagram
  participant CLI
  participant Startup
  participant LockLoader
  participant Planner
  participant Coordinator
  CLI->>Startup: provide --split-topology-lock
  Startup->>LockLoader: load and validate lock file
  LockLoader->>Planner: return resolved stage assignments
  Planner->>Coordinator: initialize locked split topology
  Coordinator->>Coordinator: withdraw after locked-stage loss
Loading

Possibly related PRs

Suggested labels: experimental

Suggested reviewers: michaelneale

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 21.15% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title is concise and accurately reflects the main change: locking split topology placement.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch jd/locked-split-topology

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@github-actions

Copy link
Copy Markdown
Contributor

This pull request is currently a draft. Reviews will not take place until the PR is marked as ready for review.

@i386
i386 marked this pull request as ready for review July 21, 2026 23:13
@github-actions
github-actions Bot requested a review from michaelneale July 21, 2026 23:13

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🧹 Nitpick comments (2)
crates/mesh-llm-host-runtime/src/runtime/split_planning.rs (1)

208-245: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Logging parity gap vs. the unlocked planning path.

plan_runtime_slice_topology_with_resources (lines 159-206) logs a "planning resource-aware split runtime topology" event before planning (with participants, layer_count, native_context_length) and a richer post-plan log including estimated_decode_network_ms_per_token/decode_tpot_target_met. plan_locked_runtime_slice_topology_with_resources only logs after a successful plan and omits both the pre-plan context and the decode-latency fields, even though TopologyPlan for the locked path carries the same estimated_decode_network_ms_per_token/decode_tpot_target_met fields (see fit_locked_candidate in locked.rs). For a benchmarking-oriented feature, matching observability would help diagnose failed/degraded locked plans.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crates/mesh-llm-host-runtime/src/runtime/split_planning.rs` around lines 208
- 245, Add logging parity to plan_locked_runtime_slice_topology_with_resources:
emit the same pre-planning resource-aware event as
plan_runtime_slice_topology_with_resources using participants, layer count, and
native context length, then include plan.estimated_decode_network_ms_per_token
and plan.decode_tpot_target_met in the successful post-plan log. Preserve the
existing locked planning and validation flow.
crates/skippy-coordinator/src/topology.rs (1)

724-803: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Add coverage for the remaining locked-topology validation error variants.

Only LockedNonContiguousRange and LockedTopologyDoesNotFit are exercised here. LockedStageCount, LockedUnknownNode, LockedDuplicateNode, LockedInvalidRange, and LockedIncompleteCoverage (all newly added in this PR at lines 89-112 and validated in locked.rs's validate_locked_stages/locked_stage_nodes) have no tests. Given this validation gates a runtime-safety-critical feature (mis-locking a topology could otherwise silently start a broken split), each rejection path is worth a dedicated regression test.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crates/skippy-coordinator/src/topology.rs` around lines 724 - 803, Add
dedicated regression tests for the remaining locked-topology validation errors
in the existing locked-topology test module: assert plan_locked_topology returns
LockedStageCount, LockedUnknownNode, LockedDuplicateNode, LockedInvalidRange,
and LockedIncompleteCoverage for minimal inputs that trigger each condition.
Match the existing assertion style and validate the expected error payloads,
while retaining the current tests for non-contiguous ranges and capacity
failures.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@crates/skippy-coordinator/src/topology.rs`:
- Around line 3-6: Move the three locked_topology_* tests from topology.rs into
a #[cfg(test)] mod tests in the locked submodule alongside plan_locked_topology
and LockedTopologyStage. Move only the imports required by those tests,
preserving their existing assertions and behavior while removing the duplicate
test code and imports from topology.rs.

In `@docs/SKIPPY_SPLITS.md`:
- Around line 93-111: Update the stage-range contract in the documented
lock-file example to explicitly state that layer_start is inclusive and
layer_end is exclusive, preserving the shown contiguous ranges and preventing
boundary ambiguity.

---

Nitpick comments:
In `@crates/mesh-llm-host-runtime/src/runtime/split_planning.rs`:
- Around line 208-245: Add logging parity to
plan_locked_runtime_slice_topology_with_resources: emit the same pre-planning
resource-aware event as plan_runtime_slice_topology_with_resources using
participants, layer count, and native context length, then include
plan.estimated_decode_network_ms_per_token and plan.decode_tpot_target_met in
the successful post-plan log. Preserve the existing locked planning and
validation flow.

In `@crates/skippy-coordinator/src/topology.rs`:
- Around line 724-803: Add dedicated regression tests for the remaining
locked-topology validation errors in the existing locked-topology test module:
assert plan_locked_topology returns LockedStageCount, LockedUnknownNode,
LockedDuplicateNode, LockedInvalidRange, and LockedIncompleteCoverage for
minimal inputs that trigger each condition. Match the existing assertion style
and validate the expected error payloads, while retaining the current tests for
non-contiguous ranges and capacity failures.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 06e748c7-78c6-456e-a984-64254ec8b3da

📥 Commits

Reviewing files that changed from the base of the PR and between a6c7ddb and 7f8ef6c.

📒 Files selected for processing (22)
  • crates/mesh-llm-cli/src/parser.rs
  • crates/mesh-llm-cli/src/parser/commands.rs
  • crates/mesh-llm-host-runtime/src/runtime/local.rs
  • crates/mesh-llm-host-runtime/src/runtime/local_split.rs
  • crates/mesh-llm-host-runtime/src/runtime/local_split/coordinator.rs
  • crates/mesh-llm-host-runtime/src/runtime/local_split/recovery.rs
  • crates/mesh-llm-host-runtime/src/runtime/local_split/test_support.rs
  • crates/mesh-llm-host-runtime/src/runtime/local_split/tests.rs
  • crates/mesh-llm-host-runtime/src/runtime/mod.rs
  • crates/mesh-llm-host-runtime/src/runtime/model_lifecycle.rs
  • crates/mesh-llm-host-runtime/src/runtime/options.rs
  • crates/mesh-llm-host-runtime/src/runtime/run_auto.rs
  • crates/mesh-llm-host-runtime/src/runtime/serving_surface.rs
  • crates/mesh-llm-host-runtime/src/runtime/split_planning.rs
  • crates/mesh-llm-host-runtime/src/runtime/split_topology_lock.rs
  • crates/mesh-llm-host-runtime/src/runtime/startup_handles.rs
  • crates/mesh-llm-host-runtime/src/runtime/startup_models.rs
  • crates/mesh-llm/src/lib.rs
  • crates/skippy-coordinator/src/topology.rs
  • crates/skippy-coordinator/src/topology/locked.rs
  • docs/SKIPPY_SPLITS.md
  • docs/design/TESTING.md

Comment on lines +3 to +6
mod locked;

pub use locked::{LockedTopologyStage, plan_locked_topology};

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win

Locked-topology tests added to topology.rs instead of the extracted module.

This PR extracts the locked-topology responsibility into a new locked submodule (re-exported here), but the tests exercising plan_locked_topology/LockedTopologyStage (lines 724-803) were added to topology.rs's own mod tests rather than to locked.rs. Repo guidelines require tests to travel with the extracted behavior when splitting a file that is already over 1,000 lines (this file now spans ~1,112 lines).

♻️ Suggested fix

Move the three locked_topology_* tests (and the LockedTopologyStage/plan_locked_topology imports they need) into a #[cfg(test)] mod tests block inside crates/skippy-coordinator/src/topology/locked.rs, alongside the extracted plan_locked_topology implementation.

As per coding guidelines, "When modifying a Rust file already over 1,000 lines, extract any separable responsibility into a named module, keep the new file under 1,000 lines, and move or add its tests with the extracted behavior."

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crates/skippy-coordinator/src/topology.rs` around lines 3 - 6, Move the three
locked_topology_* tests from topology.rs into a #[cfg(test)] mod tests in the
locked submodule alongside plan_locked_topology and LockedTopologyStage. Move
only the imports required by those tests, preserving their existing assertions
and behavior while removing the duplicate test code and imports from
topology.rs.

Source: Coding guidelines

Comment thread docs/SKIPPY_SPLITS.md Outdated

@ndizazzo ndizazzo left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

@i386 I had started chipping away at pieces of this with the config work, but right now it doesn't fully support everything you'd need for it. The idea was to incorporate the split topology in the configuration pages (which ultimate wrote some TOML like):

[[models]]
model = "hf://meshllm/example-layers@immutable-revision"

[models.model_fit]
ctx_size = 65536

[models.throughput]
parallel = 1

[models.topology]
mode = "locked"
manifest_sha256 = "0123456789abcdef..."

[[models.topology.stages]]
node = { endpoint_id = "iroh-endpoint-id-a" }
layer_start = 0
layer_end = 31

[[models.topology.stages]]
node = { hostname = "studio54-3.local" }
layer_start = 31
layer_end = 47

I think the configuration TOML is the better long-term product surface, but the existing impl can't currently express it. What would be good is to accompany this PR with a definition for how we want to transition it. Here's what Codex said about the gap:

## How far PR #1050 is from this
The PR is close at the runtime layer but not at the configuration layer:

**Already implemented:**
* immutable model/manifest verification;
* node selector resolution;
* contiguous full-model layer validation;
* capacity-aware locked planning;
* locked recovery and withdrawal behavior;
* runtime propagation.

**Still needed:**
* ModelTopologyConfig, stage, and typed node-selector structs;
* TOML serialization/deserialization;
* structured config diagnostics and canonical paths;
* built-in schema and UI metadata;
* per-model startup propagation;
* CLI-versus-config precedence;
* authoring/editor and schema fixture coverage;
* documentation and validation tests.

In the short term - nothing jumps out at me for this implementation, but I think we should definitely consider bringing them closer together from a product standpoint, so that we can take advantage of the configuration page for split configs in the web console.

@michaelneale michaelneale left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

I think very neeed - if uyou can get build passing.

@i386
i386 merged commit c67c519 into main Jul 22, 2026
42 of 43 checks passed
@i386
i386 deleted the jd/locked-split-topology branch July 22, 2026 04:04

i386 commented Jul 22, 2026

Copy link
Copy Markdown
Collaborator Author

Created #1052 to track the product transition: replace the hidden --split-topology-lock/external JSON surface with typed per-model [[models]].topology TOML configuration and web-console support, then remove the CLI option. The follow-up preserves this PR’s fail-closed validation and locked-stage recovery semantics while keeping #1050 focused on the runtime primitive.

michaelneale added a commit that referenced this pull request Jul 22, 2026
…ngram

* origin/main:
  Add GLM DSA correctness trace reporting (#1033)
  Add GLM DSA package contract validation (#1032)
  Add direct quant layer package tooling (#1031)
  Lock split topology placement (#1050)
  fix nightly stability Qwen thinking
  ui(data-mode): migrate legacy harness storage to v2 (#1040)
  fix: Windows Vulkan runtime dependencies (#1046)
  test: validate MI300 runtime selection and placement (#1045)
  fix: support ROCm RDNA APU kernels safely (#1044)
  fix: ROCm GPU enumeration (#1039)
  Document public packaging installation channels
  Fix Windows autoupdate import (#1030)
  Update llama.cpp upstream pin
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants