Tier D — Orchestrator core-extraction plan (2026-05-15)
Tier D — Orchestrator core-extraction plan (2026-05-15)
Section titled “Tier D — Orchestrator core-extraction plan (2026-05-15)”Companion to: crate-structure-audit-2026-05-15.md §4.4
Prereq: Tiers A + B + C landed (all on 2026-05-15). vox-arch-check reports clean.
vox-orchestratoris at 65,560 LoC vsmax_loc = 70_000— 7% headroom (~4,440 LoC).- The audit’s “vertical-slice” path (
agentos/→vox-orchestrator-policy) is a dead end:agentos/contains only 358 LoC across 8 stubs. - The correct wedge is C5 from the 2026-05-08 followup design: extract
src/orchestrator/(12.8K LoC) into a newvox-orchestrator-corecrate. This is the densest subdir and the primary source of the +13K LoC regrowth. - C5 requires co-moving the
Orchestratorstruct (Rust coherence:implblocks for a type must live in the defining crate). The minimum co-move set is ~8 sibling modules; total displacement is ~20–25K LoC. - Estimated post-split sizes:
vox-orchestrator-core~35K LoC,vox-orchestrator~32K LoC. - Do not start this work until
vox-orchestratorRule 13 fires (>15% LoC growth since last release tag). Until then,dei_shim/is a lower-risk intermediate extraction (§4).
1. Current state
Section titled “1. Current state”vox-orchestrator 65,560 LoC (budget: 70,000 — 7% headroom)Subdir breakdown (descending):
| Dir | LoC | Notes |
|---|---|---|
orchestrator/ | 12,825 | Task dispatch + Orchestrator inherent impls |
dei_shim/ | 5,005 | Research pipeline shim |
models/ | 3,448 | Model registry + selection |
a2a/ | 3,167 | A2A remote-worker protocol |
planning/ | 2,870 | Plan adequacy / continuation |
services/ | 2,848 | Request routing |
config/ | 2,673 | Env-sourced config |
preregistration/ | 1,456 | Agent pre-registration |
types/ | 1,426 | Shared internal types |
memory/ | 1,193 | Memory manager |
attention/ | 1,089 | Attention tracker |
session/ | 1,026 | Session lifecycle |
orch_daemon/ | 994 | Daemon entry point |
budget/ | 983 | Holistic budget manager |
queue/ | 837 | (thin, most queue logic is in vox-orchestrator-queue) |
hopper/ | 638 | Task hopper shim |
routing/ | 564 | Routing helpers |
legacy/ | 412 | Compatibility shims |
agentos/ | 358 | Policy stubs — not an extraction target |
context/ | 321 | Context store helpers |
| flat files | ~17K | runtime.rs, catalog.rs, events.rs, usage.rs, handoff.rs, … |
2. Why the agentos/ path fails
Section titled “2. Why the agentos/ path fails”The 2026-05-15 audit §4.4 listed two paths for Tier D:
Path 2 (vertical-slice): split
agentos/intovox-orchestrator-policy.
At audit-writing time agentos/ was assumed to hold most of the +13K LoC regrowth. Measurement shows it is 358 lines (8 stub files: policy_runtime.rs, guardrail_kernel.rs, context_budget_manager.rs, risk_scoring.rs, intent_planner.rs, checkpoint_engine.rs, replay_fast_forward.rs, mutation_classifier.rs). Creating a crate boundary for 358 LoC adds manifest overhead and compile-unit fragmentation with near-zero LoC benefit.
3. C5: orchestrator/ → vox-orchestrator-core
Section titled “3. C5: orchestrator/ → vox-orchestrator-core”3.1 What C5 extracts
Section titled “3.1 What C5 extracts”src/orchestrator/ contains the core Orchestrator inherent impl blocks — task dispatch submission, completion, agent lifecycle, scaling, persistence, VCS ops, campaigns, comms, safety, and the integration tests. At 12,825 LoC it is the single densest subdir and accounts for the bulk of the post-reorg regrowth.
3.2 The Rust coherence constraint
Section titled “3.2 The Rust coherence constraint”Problem: All files in src/orchestrator/ contain impl crate::orchestrator::Orchestrator { … } blocks. Rust’s coherence rules require that inherent impl blocks for a type live in the same crate as the type’s definition. You cannot add inherent methods on ExternalCrate::Orchestrator from a downstream crate.
Consequence: Extracting src/orchestrator/ requires moving the Orchestrator struct definition with it, into vox-orchestrator-core.
3.3 Sibling deps that must co-move
Section titled “3.3 Sibling deps that must co-move”src/orchestrator/**/*.rs (excluding tests) imports these sibling modules via crate:::
crate::affinity crate::attention crate::budgetcrate::bulletin crate::catalog crate::configcrate::context crate::groups crate::lockscrate::models crate::oplog crate::orchestratorcrate::planning crate::queue crate::scopecrate::services crate::snapshot crate::socratescrate::topology crate::typesNot all of these need to co-move — some can be extracted as vox-orchestrator-core dependencies instead. The break-even question is: does the module have its own Orchestrator impl blocks, or is it a pure utility consumed by those blocks?
| Module | LoC | Can stay in vox-orchestrator as a dep? | Notes |
|---|---|---|---|
types/ | 1,426 | No — must move. Used by the Orchestrator struct fields. | |
config/ | 2,673 | No — must move. OrchestratorConfig is in Orchestrator::new. | |
budget/ | 983 | No — must move. BudgetManager is a struct field. | |
locks/ | ~400 | No — must move. FileLockManager is a struct field. | |
bulletin/ | ~200 | No — must move. BulletinBoard is a struct field. | |
scope/ | ~150 | No — must move. ScopeGuard used by task dispatch. | |
groups/ | ~180 | No — must move. AffinityGroupRegistry is a struct field. | |
affinity/ | ~250 | No — must move. FileAffinityMap is a struct field. | |
context/ | 321 | No — must move. ContextStore is a struct field. | |
snapshot/ | ~150 | Possibly, if accessed via trait. | |
oplog/ | ~300 | Possibly, if accessed via trait. | |
attention/ | 1,089 | Possibly, if accessed via a dyn AttentionSink. | |
planning/ | 2,870 | Likely must move (plan adequacy checks are in completion handlers). | |
services/ | 2,848 | Likely must move (routing is called in task dispatch). | |
catalog/ | 853 | Likely must move (catalog refresh is an impl block). | |
models/ | 3,448 | Likely must move (model selection called in dispatch). | |
socrates/ | ~546 | Possibly, via trait injection. | |
topology/ | ~200 | Small — probably must move. | |
queue/ | 837 | Already thin; most logic in vox-orchestrator-queue. |
Estimated minimum co-move set: types + config + budget + locks + bulletin + scope + groups + affinity + context ≈ ~6,600 LoC. With planning + services + catalog + models (likely must move), the co-move set grows to ~17K LoC.
3.4 Estimated post-split sizes
Section titled “3.4 Estimated post-split sizes”| Crate | Post-split LoC | Budget |
|---|---|---|
vox-orchestrator-core | ~35,000 | suggest max_loc = 40_000 |
vox-orchestrator | ~32,000 | suggest max_loc = 35_000 (down from 70K) |
The split roughly halves both crates and leaves vox-orchestrator holding: orch_daemon/, dei_shim/, a2a/, runtime.rs, preregistration/, session/, hopper/, routing/, legacy/, and the integration glue.
4. Interim option: dei_shim/ extraction
Section titled “4. Interim option: dei_shim/ extraction”If C5 is too large to scope right now, dei_shim/ (5,005 LoC) is a viable intermediate:
- It is the research pipeline shim (
vox dei, MENS pipeline dispatch,pipeline.rsat 930 LoC). - Its main consumer is
orch_daemon/dei_dispatch.rs(323 LoC). - Probable dep set:
vox-orchestratortypes,vox-db,vox-ml-cli,vox-compiler. - If it can accept
Orchestratorvia a thin trait (5–10 methods: task_submit, attention_report, etc.), it becomes extractable without moving the struct. - Estimated gain: −5K LoC from
vox-orchestrator, buying ~3 more months of headroom at the observed growth rate.
Recommended: do dei_shim/ extraction only if Rule 13 fires before C5 is staffed, as a holding action.
5. Task breakdown for C5
Section titled “5. Task breakdown for C5”Prerequisite: Rule 13 must confirm
vox-orchestratorhas grown >15% since last release tag before starting. If the budget is still comfortable, defer.
D1 — Dep audit (1–2h)
Section titled “D1 — Dep audit (1–2h)”For each module in the co-move candidate list (§3.3), run:
grep -rn "crate::<module>" crates/vox-orchestrator/src/orchestrator/ --include="*.rs" | wc -lClassify each as: must-move / trait-injectable / stays-as-dep. Produce a final co-move manifest and target sizes. Update this doc with the findings.
D2 — Create vox-orchestrator-core skeleton (2h)
Section titled “D2 — Create vox-orchestrator-core skeleton (2h)”crates/vox-orchestrator-core/ Cargo.toml (name = "vox-orchestrator-core", layer = 3, max_loc = 40_000) src/ lib.rs- Add to
Cargo.tomlworkspace members (via glob, no manual edit needed). - Add to
layers.tomlandwhere-things-live.md. - Add
workspace-hackdep. - Add to hakari
traversal-excludesif it ends up smaller than 2K LoC (it won’t — skip).
D3 — Move co-move modules (1–2d)
Section titled “D3 — Move co-move modules (1–2d)”For each module in the co-move manifest:
git mv crates/vox-orchestrator/src/<mod> crates/vox-orchestrator-core/src/<mod>- Rewrite
use crate::<mod>→use crate::<mod>(stayscrate::withinvox-orchestrator-core). - Update
vox-orchestrator/src/lib.rstopub use vox_orchestrator_core::<mod>;for any type that’s in the public API. - Add
vox-orchestrator-core = { path = … }tovox-orchestrator/Cargo.toml.
D4 — Move orchestrator/ subdir (4–8h)
Section titled “D4 — Move orchestrator/ subdir (4–8h)”git mv crates/vox-orchestrator/src/orchestrator crates/vox-orchestrator-core/src/orchestrator- Fix all
crate::refs withinorchestrator/(they now point tovox-orchestrator-coreroot — should still resolve after D3 moved the deps). - Move the
Orchestratorstruct definition fromvox-orchestrator/src/lib.rstovox-orchestrator-core/src/lib.rs. - Re-export in
vox-orchestrator/src/lib.rs:pub use vox_orchestrator_core::Orchestrator; - Fix any remaining compilation errors.
D5 — Integration glue (2–4h)
Section titled “D5 — Integration glue (2–4h)”vox-orchestrator/src/runtime.rsandorch_daemon/createOrchestratorinstances — they now import fromvox-orchestrator-core. VerifyOrchestrator::newis accessible.vox-cli→vox-orchestratorinversion: the known inversion does not change (stillvox-cli→vox-orchestrator). The build-time benefit comes fromvox-orchestratornow being thinner.
D6 — Tests (2h)
Section titled “D6 — Tests (2h)”- Integration tests in
src/orchestrator/tests/stay with the struct invox-orchestrator-core. cargo test -p vox-orchestrator-coremust pass.cargo test -p vox-orchestratormust pass.cargo run -p vox-arch-checkmust report clean.
D7 — Cleanup (1h)
Section titled “D7 — Cleanup (1h)”- Update
layers.toml: addvox-orchestrator-coreentry, lowervox-orchestratormax_locfrom 70K to 35K. - Update
where-things-live.md: addvox-orchestrator-corerow in L3 section. - Update this plan doc: mark complete, record actual post-split LoC counts.
6. Decision checklist before starting
Section titled “6. Decision checklist before starting”-
vox-arch-checkRule 13 has fired forvox-orchestrator(>15% LoC growth since last tag), OR headroom has dropped below 5% (~3,500 LoC). - D1 dep audit is complete and the co-move manifest is finalized.
- No other active large PR touches
vox-orchestrator/src/(merge conflicts would be severe). - CI is green on
mainbefore branching.
7. Risk register
Section titled “7. Risk register”| Risk | Likelihood | Impact | Mitigation |
|---|---|---|---|
Orchestrator struct has hidden dependencies not caught in D1 | Medium | High — compile failure mid-D4 | D1 must use cargo check iteration, not grep alone |
Re-export changes break the vox-cli → vox-orchestrator inversion | Low | Medium — arch-check flags it | Verify known_inversions in layers.toml still covers the edge |
dei_shim/ has transitive dep on Orchestrator impl methods (not just the type) | Medium | Medium — blocks dei_shim interim option | Check with cargo check -p vox-orchestrator-core excluding dei_shim |
Integration tests in orchestrator/tests/ require types from vox-orchestrator (post-split) | Medium | Low — test-only fix | Move to vox-orchestrator tests/ as integration test using both crates |
| Merge conflicts if MENS/mesh work lands while Tier D is in progress | High | High | Coordinate with MENS Mn-T1 timeline; Tier D should not overlap |
8. Appendix: commands for re-measuring
Section titled “8. Appendix: commands for re-measuring”# Total LoC(Get-ChildItem crates/vox-orchestrator/src -Recurse -Filter *.rs | Get-Content | Measure-Object -Line).Lines
# Per-subdirGet-ChildItem crates/vox-orchestrator/src -Directory | ForEach-Object { $loc = (Get-ChildItem "$($_.FullName)" -Recurse -Filter *.rs | Get-Content | Measure-Object -Line).Lines "$loc $($_.Name)"} | Sort-Object { [int]($_ -split '\s+')[0] } -Descending
# agentos/ specifically(Get-ChildItem crates/vox-orchestrator/src/agentos -Recurse -Filter *.rs | Get-Content | Measure-Object -Line).Lines