Use when wiring a just-built ServingStudio Sim L4 arch model into the shared dispatch sites so it is selectable by tag, predictable via timing-predict, and ready for a future worker — the integration step AFTER impl-compose-arch has produced the arch file. Covers arch/mod.rs re-exports, the arch/build.rs concrete builder + its uniform predictor dispatch (build_iter_model / build_attn_model / build_ffn_model), and the deployment build arm (worker pairing or explicit bail). The completion proof is a green timing-predict run on the new arch. Does NOT author the arch cost file / trait impl (that is impl-compose-arch) or build a worker.
Install
npx skillscat add syfi-servingstudio/servingstudiosim/impl-wire-new-arch Install via the SkillsCat registry.
Impl Wire New Arch
You are the implementer for the cross-file integration that turns a buildable
L4 arch into a reachable, predictable one. impl-compose-arch produced the arch
model file (arch/<family>.rs: build_configs → resolve_configs → build, the
compiled CostTree, and the contract-trait impl) and declared its config.rs
selector variant. Your job is to connect that model to the shared dispatch sites
so it can be selected by type:, costed by timing-predict, and later run by a
worker with no further arch surgery.
The through-line of top-add-new-arch is where the timing comes from. This
phase does not add timing — it wires the composed timing so a caller can evaluate
it. The tightest such caller is timing-predict (no worker, no DES), which is
why this phase exists before Validate: it makes the new arch predictable the
moment its cost model builds.
Precondition (from impl-compose-arch)
Do not start until all hold:
simulator/src/arch/<family>.rsbuilds and its unit tests pass in isolation;pub mod <family>;is insimulator/src/arch/mod.rs;- the
config.rsselector variant parses (just test-cpuconfig round-trip green); - the model impls the right contract trait for its kind:
- iter →
IterwiseUnifiedModel; attn →AttnLayerwiseModel; ffn →FfnLayerwiseModel
(arch/contract.rs). If it does not, that is impl-compose-arch's leaf, not this one.
- iter →
Read First
simulator/src/arch/README.md(the L4↔L5 contract) andarch/contract.rs;simulator/src/arch/build.rs(thebuild_iter_model/build_attn_model/build_ffn_modelpredictor matches + the concretedense/qwen3_moe/qwen3_attn/qwen3_ffn_moebuilders — copy the closest);simulator/src/timing_predict.rs(PredictArchSeland therun_iter_cases/run_attn_cases/run_ffn_casesdrivers that call thebuild_*_modelseam);- the matching deployment:
deployment/unified.rs+deployment/pd.rs(iter),
ordeployment/afd.rs(attn/ffn); anddeployment/config.rs(the pool maps).
The wiring surface — the compiler is your checklist
Adding a selector variant makes the exhaustive matches below fail to compile
until you add its arm. Let the build errors drive you; there is no central
registry to update (the launcher list-params schema is derived from#[derive(ProviderSchema)] + #[param], so a new tag surfaces with no manual arm).
Compiler-forced (no catch-all — you MUST add an arm):
| Site | iter | attn | ffn |
|---|---|---|---|
arch/config.rs — *ArchSel::model() |-chain |
✔ | ✔ | ✔ |
arch/build.rs — build_{iter,attn,ffn}_model match (the predictor seam) |
✔ | ✔ | ✔ |
deployment/unified.rs — match &g.arch |
✔ | — | — |
The predictor dispatch is uniform: all three kinds go through abuild_*_model match in arch/build.rs (each returns Box<dyn …LayerwiseModel> /Box<dyn IterwiseUnifiedModel>). timing_predict.rs just calls those and needs
no per-arch edit. So the rule is one-liner: one predictor arm inarch/build.rs + one deployment arm — for every arch kind.
Catch-all bail! (compiles without you; add an arm only to enable that deployment):deployment/pd.rs (iter, tuple match on prefill/decode) and deployment/afd.rs
(attn/ffn, tuple match on attn/ffn). Leaving these unwired is fine for a
timing-predict-only milestone — the arch still predicts; only a real run bails.
Two arms, two consumers — wire both. Every arch is built from TWO places
that share the same concretearch_build::<family>builder but dispatch
independently: the predictor arm (build_*_modelinarch/build.rs,
boxed, no worker) and the deployment arm (unified.rs/pd.rs/afd.rs, a
concrete-typed worker flow). Wiring only one means a green timing-predict with a
bailing real run, or vice-versa. This shape is now identical for iter and AFD —
the predictor seam lives inarch/build.rsfor all three kinds (it used to be
split off intiming_predict.rsfor AFD; that asymmetry was removed so a second
AFD arch lands as one more match arm, not a signature break).
Expected Write Scope
arch/mod.rs—pub use <family>::{<Model>, <Parallel>};sobuild.rscan name
the concrete type (thepub modline is already there from the precondition).arch/build.rs— a concrete builderpub fn <family>(model_spec, <parallel params>, gpu, name, bridge) -> Result<<Model>>mirroring the closest existing
one (ModelSpec→dense_model_cfg/moe_model_cfg, build the*Parallel
struct,resolve_configs(build_configs(...)),build(name, resolved, bridge)
with a.context(...)naming the likely missing-profile.db-row failure). Then
add its predictor arm to the matchingbuild_iter_model/build_attn_model
/build_ffn_model(Box::new(<family>(...)?)) — the uniform seam both the
deployment and the predictor build from.timing_predict.rs— no per-arch edit. It callsbuild_{iter,attn,ffn}_model
generically, so thearch/build.rspredictor arm above is all prediction needs
(for any kind — iter and AFD alike).arch/config.rs— extend themodel()|-chain to include the new variant
(compiler-forced). Confirm the variant's#[param(cache_key)]flags are right
(model identity / dtype / any param that changes which kernels are needed).- deployment arm — pave the road for the worker: add the deployment
build
arm that constructs the model via your samearch_build::<family>concrete
builder and pairs it with a worker. If the worker does not exist yet,bail!
with an explicit"<deployment>: <arch> requires worker <X>, not wired yet"so
the future worker dev is a drop-in (it just fills this arm). Forunified.rs
(compiler-forced) you must add some arm now — a clearbail!is acceptable.
Do NOT edit the arch cost file, add a worker impl, change contract.rs traits, or
touch the launcher schema derive. Stop and return to the orchestrator if the arch
does not fit an existing contract trait or needs a new PredictArchSel family.
Tests And Smoke
Build + CPU tier (config round-trip, arch unit tests, dispatch compiles):
uv run cargo build --release -p simulator
just test-cpuConfirm the tag is in the launcher schema (derived — should appear with no arm):
uv run python -m launcher list-params | grep -i <tag>The completion proof — timing-predict runs green on the new arch. Author a
minimal predict config (see operate-run-timing-predict; templates inpresets/predict_qwen3_235b_*) selecting the new arch and a couple of cases:
cd "$(git rev-parse --show-toplevel)"
uv run python -m launcher timing-predict presets/<new_arch_predict>.jsonRun it bare — do not pin CUDA_VISIBLE_DEVICES / VIBESIM_PROFILE_GPUS to
limit the device set unless you have a concrete reason. L1 does idle-GPU
detection and arrangement itself (find_idle_gpus in profiling/exec/local.py);
narrowing the set only serializes a fill that could have gone parallel and
hard-fails when the pinned card is busy. Likewise, do not hand-enumerate the
missing profile.db rows for the new arch with kernel-profile count-missing /run — the prediction's own cold-cache path enumerates exactly what the compiled
cost tree looks up.
Read logs/<dir>/reports/iter_breakdown.ans: every element from the Phase-1
decision table must appear as a leaf with a non-zero, plausibly-scaled timing (no
missing kernel, no zero, no absurd µs). A cold profile.db JIT-fills on whatever
idle GPUs L1 picks; a warm one needs none.
Report Back
Return: the arch kind (iter/attn/ffn) and selector tag; files + match arms added;
the concrete build.rs builder signature; whether the deployment arm is wired or
bails (and which worker it awaits); list-params confirmation; and the
timing-predict smoke result (log dir + per-case totals, or the first missing/zero
leaf if the cost model is incomplete — that is an impl-compose-arch gap, not a
wiring one).