Status: draft
Layer package repositories are durable model artifacts for skippy-backed stage
serving. A repository contains one model-package.json manifest plus GGUF
fragments that can be selected by layer range and loaded by a stage without
requiring every peer to store or materialize the full source model.
The current runtime accepts local package directories and Hugging Face package
references of the form hf://namespace/repo, hf://namespace/repo:revision,
or hf://namespace/repo@revision.
- Let mesh nodes fetch only the model pieces needed for their assigned layer range.
- Keep package identity tied to a real source model coordinate, revision, and artifact set.
- Make package validation deterministic before a stage is launched.
- Keep package manifests additive so older runtimes can reject unsupported packages clearly instead of loading incompatible tensor layouts.
- Treat per-stage materialized GGUFs as derived cache, not as the durable package format.
A layer package repository SHOULD be named after the source model and distribution it contains, for example:
meshllm/Qwen3-235B-A22B-UD-Q4_K_XL-layers
meshllm/DeepSeek-V3.2-UD-Q4_K_XL-layers
The repository identity is not enough to prove compatibility. Consumers MUST
read model-package.json and use the manifest fields below as the source of
truth.
Package references use hf:// so runtime code can distinguish package repos
from model coordinates and local paths:
hf://meshllm/Qwen3-235B-A22B-UD-Q4_K_XL-layers
hf://meshllm/Qwen3-235B-A22B-UD-Q4_K_XL-layers:8f4c2d1
hf://meshllm/Qwen3-235B-A22B-UD-Q4_K_XL-layers@main
Publishers SHOULD point production configs at an immutable commit hash or tag, not a moving branch.
The root of the repository MUST contain model-package.json.
Recommended layout:
model-package.json
shared/
metadata.gguf
embeddings.gguf
output.gguf
layers/
layer-00000.gguf
layer-00001.gguf
layer-00002.gguf
...
projectors/
mmproj-model-f16.gguf
README.md
Required artifacts:
| Artifact | Purpose |
|---|---|
shared/metadata.gguf |
Shared GGUF metadata and tokenizer state required by every stage. |
shared/embeddings.gguf |
Input-boundary tensors required by the first stage. |
shared/output.gguf |
Output-boundary tensors required by the final stage. |
layers/layer-NNNNN.gguf |
Owned tensors for one transformer layer. |
Optional artifacts:
| Artifact | Purpose |
|---|---|
projectors/*.gguf |
Multimodal projector GGUFs, currently kind: "mmproj", used by stage 0 or single-stage serving. |
Artifact paths in the manifest MUST be relative to the repository root. They
MUST NOT be absolute paths and MUST NOT escape the package root with ...
Consumers MUST reject unsafe paths.
Each owned tensor from the source model MUST appear in exactly one package artifact. Shared metadata and tokenizer values may be repeated only where the GGUF writer requires them to keep each fragment loadable.
Projector artifacts are package-level companions, not transformer-layer fragments. They MUST NOT be counted as owned layer tensors and MUST NOT be merged into per-stage GGUF materializations.
For split Skippy runs, a worker may fetch missing Hugging Face package artifacts
from the coordinating mesh node over admitted mesh STREAM_SUBPROTOCOL
transport before falling back to normal local/HF package resolution. This
transfer path is an optimization for already-selected split participants, not a
package discovery protocol.
Privacy and compatibility boundaries:
- Nodes advertise only
skippy-stage/2subprotocol feature support, includingartifact-transfer. They do not gossip local package inventory, artifact paths, cache roots, or tokens. - Mesh owns only the subprotocol open envelope; Skippy owns the artifact request/response schema, authorization semantics, and byte framing.
- Peer cache transfer uses the mesh
STREAM_SUBPROTOCOLenvelope. Generation-7 Skippy stage peers require the complete control/status/content-identity capability bundle, so older or partially capable subprotocol peers are not mixed into new split topologies. - Only
hf://namespace/repo@revisionpackage refs are eligible for peer transfer. - The serving node checks the active split topology and only serves artifacts needed by the requesting node's assigned stage range. Stage 0 may fetch input boundary files and projector artifacts; final stages may fetch output boundary files.
model-package.jsonis capped at 16 MiB for peer transfer.- Non-manifest artifacts must match the manifest-declared relative path, byte size, and SHA-256 digest.
- Received artifacts are written to a fresh hidden partial file and installed atomically only after size and SHA-256 verification.
- Peer artifact transfer is not advertised or served by default on public mesh
nodes. Set
MESH_LLM_ARTIFACT_TRANSFER=trustedto enable same-owner or explicitly trusted-owner transfer, orMESH_LLM_ARTIFACT_TRANSFER=openfor lab deployments that intentionally allow any peer.
The manifest file is UTF-8 JSON. The current schema version is 1.
Minimal shape:
{
"schema_version": 1,
"model_id": "Qwen/Qwen3-235B-A22B-GGUF:UD-Q4_K_XL",
"source_model": {
"path": "/cache/Qwen3-235B-A22B-UD-Q4_K_XL.gguf",
"sha256": "<64 hex chars>",
"repo": "Qwen/Qwen3-235B-A22B-GGUF",
"revision": "<source commit>",
"primary_file": "Qwen3-235B-A22B-UD-Q4_K_XL.gguf",
"canonical_ref": "Qwen/Qwen3-235B-A22B-GGUF:UD-Q4_K_XL",
"distribution_id": "UD-Q4_K_XL",
"files": [
{
"path": "Qwen3-235B-A22B-UD-Q4_K_XL.gguf",
"size_bytes": 123,
"sha256": "<64 hex chars>"
}
]
},
"format": "layer-package",
"layer_count": 94,
"activation_width": 8192,
"shared": {
"metadata": {
"path": "shared/metadata.gguf",
"tensor_count": 0,
"tensor_bytes": 0,
"artifact_bytes": 123,
"sha256": "<64 hex chars>"
},
"embeddings": {
"path": "shared/embeddings.gguf",
"tensor_count": 4,
"tensor_bytes": 123,
"artifact_bytes": 123,
"sha256": "<64 hex chars>"
},
"output": {
"path": "shared/output.gguf",
"tensor_count": 4,
"tensor_bytes": 123,
"artifact_bytes": 123,
"sha256": "<64 hex chars>"
}
},
"generation": {
"policy": {
"profile": "glm-dsa-v1",
"decode": "compact-flash",
"short_prefill": "dense",
"long_prefill": "sparse-chunked",
"verify": "auto",
"indexshare": "required",
"experimental": {
"selected_row_flash": "evidence-gated",
"moe_weighted_down": "evidence-gated",
"moe_merged_shared_gate_up": "evidence-gated"
}
},
"thresholds": {
"short_prefill_max_tokens": 2048,
"compact_flash_min_kv": 1,
"dense_mask_max_bytes": 268435456
},
"speculative_decoding": {
"default": "mtp",
"strategies": {
"mtp": {
"type": "native-mtp",
"prediction_depth": 1,
"layer_indices": [47],
"window_policy": {
"default": "fixed",
"initial_window": 1,
"min_window": 1,
"max_window": 1,
"pipeline_depth": 1
}
}
}
}
},
"layers": [
{
"layer_index": 0,
"path": "layers/layer-00000.gguf",
"tensor_count": 32,
"tensor_bytes": 123,
"artifact_bytes": 123,
"sha256": "<64 hex chars>"
}
],
"projectors": [
{
"kind": "mmproj",
"path": "projectors/mmproj-model-f16.gguf",
"tensor_count": 128,
"tensor_bytes": 123,
"artifact_bytes": 123,
"sha256": "<64 hex chars>"
}
],
"skippy_abi_version": "1.2.3",
"created_at_unix_secs": 1790000000
}Required top-level fields:
| Field | Requirement |
|---|---|
schema_version |
MUST be 1 for the current format. |
model_id |
MUST be a non-empty model coordinate, not a filesystem-derived name. |
source_model |
MUST identify the source artifact used to build the package. |
format |
MUST be layer-package. |
layer_count |
MUST match the source model's transformer layer count. |
activation_width |
SHOULD be present as a scalar pre-load estimate for topology diagnostics. It is not an activation-frame contract, and reviewed family data takes precedence. |
generation |
MAY declare package-owned generation defaults, including native speculative decoding strategies. |
shared |
MUST include metadata, embeddings, and output artifacts. |
layers |
MUST include exactly one entry for each layer index 0..layer_count. |
projectors |
MAY include package-level projector artifacts; currently only kind: "mmproj" is defined. |
skippy_abi_version |
MUST describe the llama/skippy ABI used to write the fragments. |
created_at_unix_secs |
SHOULD be set by the package writer for provenance. |
Each artifact entry MUST include:
path: repository-relative artifact path.tensor_count: number of tensors in the fragment.tensor_bytes: total bytes for tensor payloads in the fragment.artifact_bytes: exact file size in bytes.sha256: lowercase or uppercase 64-character SHA-256 hex digest.
artifact_bytes MUST be greater than zero. tensor_bytes MUST be zero when
tensor_count is zero and greater than zero when tensor_count is greater
than zero.
projectors is optional and defaults to an empty list. When present, each
projector entry uses the same artifact fields plus:
kind: projector type. The current schema definesmmproj; consumers MUST reject unknown kinds unless they explicitly support them.path: repository-relative projector GGUF path, usually underprojectors/.
Projector entries MUST have non-empty, safe relative paths, positive
artifact_bytes, and valid 64-character SHA-256 digests. A package MAY declare
multiple projectors for future model variants, but the current serving default
is to use the first declared mmproj when no explicit projector path is
configured.
generation is optional and defaults to no package-owned generation policy.
When present, it may declare package-authored runtime defaults. The package owns
defaults that are specific to the artifact distribution, such as quant layout,
preserved native tensors, validated sparse-attention paths, and native
speculative decoding strategy.
The generation object has two separate responsibilities:
generation.policynames the semantic execution profile and phase choices that were validated for this artifact.generation.thresholdssupplies numeric resolver hints used to decide when a phase choice applies.generation.speculative_decodingdeclares package-owned native or draft speculation strategy defaults, when the artifact actually contains or references the required tensors/models.
Use this split consistently:
| Location | Meaning | Typical fields |
|---|---|---|
generation.policy |
Portable, semantic execution decisions for this artifact. | profile, decode, short_prefill, long_prefill, verify, indexshare |
generation.policy.experimental |
Named experimental policy paths that are valid only with explicit evidence and logging. | selected_row_flash, moe_weighted_down, moe_merged_shared_gate_up |
generation.thresholds |
Numeric decision inputs for the policy resolver. | token limits, byte limits, minimum KV sizes |
generation.speculative_decoding |
Package-owned speculation strategy defaults. | default, strategies, native-mtp, prediction_depth, window_policy |
| GGUF metadata | Architecture and tensor-layout correctness contract. | attention dimensions, IndexShare roles, native MTP tensor layout |
Keep these responsibilities separate. Policy fields should answer "which
execution path is intended for this phase?" Threshold fields should answer
"when should the runtime choose or reject that path?" For example,
decode: "compact-flash" belongs under generation.policy, while
compact_flash_min_kv: 1 belongs under generation.thresholds.
The generation object is intentionally generic. Do not add
model-family-specific sub-objects such as generation.glm_dsa; use a stable
generation.policy.profile instead. This keeps the manifest shape reusable for
future sparse attention, native prediction, and verifier policies without
creating one schema branch per model family.
Keep backend tuning out of the manifest unless it can be expressed as portable
policy or threshold data. Kernel names, Metal/CUDA dispatch choices, simdgroup
tuning, row-height sweeps, and quant-kernel experiments are runtime capability
or benchmark evidence. They should not become manifest fields such as
generation.metal or generation.glm_dsa. When evidence graduates into a
package default, record it as a profile version, a phase policy, a numeric
threshold, or a speculation strategy.
Runtime config and explicit CLI/environment overrides MAY override these defaults for experiments, but consumers SHOULD log the final resolved policy and the package recommendation that was overridden. If a consumer cannot execute the package-recommended path, it MUST choose a correctness-preserving fallback and emit the fallback reason.
Generation policy is not a backend feature switch. Package authors MUST NOT use backend-specific fields to select Metal, CUDA, CPU, Vulkan, or Skippy kernels. Backends advertise capability and performance evidence to the resolver; the manifest records the package-level semantic policy and thresholds that make the resolver decision explainable.
Consumers MUST NOT silently reinterpret unknown policy values as a supported path. Unknown profiles, phase values, or experimental policy switches should be reported as unsupported unless the runtime has an explicit compatibility rule for that value. Numeric threshold fields are hints and may be ignored by older consumers, but policy fields describe execution semantics and must be handled deliberately.
Package writers SHOULD only emit a generation.policy.profile after they have
validated the artifact shape that profile requires. For GLM-DSA this means the
writer has found the GLM-DSA attention tensors, routed/shared MoE tensors,
IndexShare metadata or equivalent role evidence, and any preserved native MTP
tensors before advertising glm-dsa-v1. The writer may infer the default
policy from GGUF metadata and tensor names, but it should not infer a more
specific policy than the artifact can actually support.
Serving resolvers SHOULD treat generation as input to a phase resolver, not
as direct kernel wiring. A resolver first identifies the request phase, such as
decode, short prefill, long prefill, or speculative verification. It then
combines the package policy, thresholds, backend capability evidence, and
request shape to select a path. The selected path, rejected package path, and
fallback reason should be visible in logs or telemetry whenever the resolver
does not use the package recommendation.
Package generation defaults are not a substitute for model correctness
metadata. Architecture-specific GGUF metadata and tensor layout still define
whether a runtime may execute the model at all; generation.policy only
chooses among valid execution paths for that artifact.
Packages MAY declare generation.policy to describe the package-validated
execution profile. This profile is a model execution policy, not a backend
implementation detail. It should use stable semantic path names such as
compact-flash rather than Metal/CUDA kernel names.
The current proposed shape is:
{
"generation": {
"policy": {
"profile": "glm-dsa-v1",
"decode": "compact-flash",
"short_prefill": "dense",
"long_prefill": "sparse-chunked",
"verify": "auto",
"indexshare": "required",
"experimental": {
"selected_row_flash": "evidence-gated"
}
},
"thresholds": {
"short_prefill_max_tokens": 2048,
"compact_flash_min_kv": 1,
"dense_mask_max_bytes": 268435456
}
}
}profile names the policy family and version. For GLM-DSA packages, use
glm-dsa-v1 until a later profile intentionally changes the meaning of the
phase fields or thresholds. Other model families SHOULD use their own stable
profile names instead of adding model-family-specific top-level objects under
generation.
The profile is also the compatibility boundary for package tooling. Writers may
infer a known profile from GGUF metadata and tensors, but they must not invent
backend-specific field names for one model family. If a later GLM-DSA package
needs different phase semantics, create a new profile such as glm-dsa-v2
instead of changing the meaning of glm-dsa-v1.
Policy values are intentionally phase-specific:
decode: preferred one-token generation path. For GLM-DSA this is expected to becomecompact-flashwhen compact selected-KV attention has parity on the package.short_prefill: preferred path below the short-prefill threshold. Packages MAY selectdensewhen sparse/indexer overhead is known to dominate.long_prefill: preferred path above the short-prefill threshold. Packages SHOULD avoid policies that materialize dense sparse masks for long context.verify: preferred path for speculative verification spans. It MAY remainautountil verifier-specific parity and performance are measured.indexshare: whether Shared GLM-DSA layers require local IndexShare/top-k state.requiredmeans a consumer must not silently recompute shared-layer indexers unless an explicit fallback policy is selected and logged.experimental.selected_row_flash: controls selected-row flash fusion. Useevidence-gateduntil the package has reproducible wins for that path on the target backend.experimental.moe_weighted_down: controls moving MoE route weights before the routed down projection instead of applying them in the output weighted sum. Useevidence-gated; current evidence makes this a small graph-shape experiment, not a replacement for expert matmul optimization.experimental.moe_merged_shared_gate_up: controls merged shared-expert gate/up execution. Useevidence-gateduntil the package/backend has reproducible combined-FFN graph wins and parity evidence.
Suggested semantic path values are:
auto: runtime chooses using package thresholds and backend capability.dense: dense attention path; useful for short prefill when measured faster.direct-sparse: direct GLM-DSA sparse attention.compact-flash: compact selected K/V followed by flash attention.sparse-chunked: chunked sparse prefill path for long prompts.fallback: named correctness fallback when a native sparse backend is not available.
generation.thresholds are package recommendations. Consumers SHOULD treat
them as input to the runtime policy resolver, not as hard schema limits. Every
policy decision SHOULD emit telemetry containing the policy profile, phase,
selected path, rejected path or fallback reason, n_kv, top_k when present,
IndexShare role when present, backend, and any dense sparse-mask allocation
avoided.
Thresholds must be named for the resolver decision they inform, not for an
implementation detail. For example, prefer dense_mask_max_bytes over a
backend-specific allocation flag. This keeps the same package usable across
Metal, CUDA, CPU, and future backends while still allowing each backend to make
an evidence-based decision.
Threshold units are part of the field contract:
| Threshold | Unit | Resolver use |
|---|---|---|
short_prefill_max_tokens |
tokens | Selects the short-prefill policy when the prompt/window is at or below this size. |
compact_flash_min_kv |
KV rows | Rejects compact selected-KV flash below the minimum useful KV history. |
dense_mask_max_bytes |
bytes | Rejects dense sparse-mask materialization when the estimated mask would exceed this budget. |
Consumers resolve generation policy in this order:
- Request/runtime override, when explicitly configured for an experiment.
- Package
generation.policyandgeneration.thresholds. - Runtime built-in default for the architecture.
- Correctness fallback when the preferred path is unsupported.
The resolved policy is a runtime contract. Package tools may infer and write
the policy from GGUF tensor shape, but serving code must not re-infer a
different policy silently. For example, a GLM-DSA package with split
attn_k_b, attn_v_b, and attn_kv_a_mqa tensors may advertise
glm-dsa-v1; the runtime may still fall back from compact-flash to dense
on a backend that lacks compact selected-KV attention, but it must log that
fallback.
Policy resolution telemetry SHOULD include the package profile and the exact
decision inputs that changed the selected path: phase, backend, n_tokens,
n_kv, top_k, estimated dense-mask bytes, selected threshold, selected path,
fallback path, and fallback reason. This is required evidence for tuning
package defaults; without it, benchmark results cannot explain whether a run
used the intended GLM-DSA path or a correctness fallback.
For glm-dsa-v1, the current phase intent is:
| Phase | Recommended value | Intent |
|---|---|---|
decode |
compact-flash |
Avoid dense sparse-mask materialization during one-token decode. |
short_prefill |
dense |
Avoid paying sparse/indexer overhead when prompts are below the package threshold. |
long_prefill |
sparse-chunked |
Keep long-context prefill away from huge dense sparse masks. |
verify |
auto |
Let the runtime select a verifier path until verifier-specific parity is proven. |
indexshare |
required |
Reuse Full-layer top-k/index state for Shared GLM-DSA layers instead of silent recompute. |
experimental.selected_row_flash |
evidence-gated |
Enable compact selected-row flash only when package/backend evidence proves parity and a win. |
experimental.moe_weighted_down |
evidence-gated |
Enable weighted-down MoE graph shape only when package/backend evidence proves parity and a win. |
experimental.moe_merged_shared_gate_up |
evidence-gated |
Enable merged shared-expert gate/up only when package/backend evidence proves parity and a combined-graph win. |
For glm-dsa-v1, the current threshold intent is:
| Threshold | Meaning |
|---|---|
short_prefill_max_tokens |
Maximum prompt/window length that should prefer the short-prefill policy. |
compact_flash_min_kv |
Minimum KV length where compact selected-KV flash attention is worth considering. |
dense_mask_max_bytes |
Maximum dense sparse-mask allocation the runtime should permit before forcing a sparse fallback. |
For GLM-DSA threshold tuning, consumers SHOULD reason from the package tensor sizes rather than from model names alone. The relevant first-order estimates are:
| Quantity | Formula | Example |
|---|---|---|
| Hidden activation bytes | tokens * hidden_width * activation_bytes |
GLM-5.2 width 6144: 12 KiB/token at f16 or 24 KiB/token at f32. |
| IndexShare sideband bytes | tokens * top_k * 4 |
Width 768: 3 KiB/token, 384 KiB for a 128-token chunk. |
| Dense sparse-mask bytes | tokens * visible_kv * 4 |
At 128k visible KV: 512 KiB/token, 64 MiB for a 128-token chunk, 1 GiB for a 2048-token chunk. |
These numbers are intentionally policy inputs, not schema requirements. A
package with a different attention.indexer.top_k, hidden width, activation
wire dtype, or context target will produce different thresholds. The important
contract is that packages expose enough policy and threshold information for
the runtime to explain why it selected dense, direct sparse, compact-flash, or
fallback execution.
Current GLM-DSA tuning is grounded in llama.cpp Metal backend fixtures around
kv=257,top_k=64. On the one-token decode shape, compact selected-row flash
measured 63.40 us/run, direct sparse attention measured 106.57 us/run, and
dense masked flash measured 71.72 us/run. That makes the compact selected-row
path about 1.68x faster than direct sparse and about 1.13x faster than dense
masked flash for this fixture.
Exact-boundary fixtures where selected KV equals visible KV are more decisive
than the base one-token fixture. Compact selected-row flash measured
61.90 us/run at kv=128,top_k=128, 60.71 us/run at
kv=256,top_k=256, 61.24 us/run at kv=257,top_k=257, and
57.99 us/run at kv=513,top_k=513. Direct sparse measured
137.15 us/run, 209.97 us/run, 249.30 us/run, and 711.10 us/run on the
same shapes. That makes compact selected-row flash about 2.2x, 3.5x,
4.1x, and 12.3x faster than direct sparse across those exact-boundary
points. The literal top_k >= visible_kv boundary is a separate all-KV flash
bypass in the llama.cpp graph, but ordinary one-token decode after prefill
still exercises compact selected-KV flash because IndexShare top-k is selected
from the previous KV state while attention sees previous+current KV. This is
why glm-dsa-v1 uses compact flash as the default decode route and reserves
direct sparse decode for explicit runtime experiments.
For the configured GLM-5.2 IndexShare width, the compact path is more
important once visible KV grows beyond the 768 selected rows. The Metal
fixtures measured compact selected-row flash at 55.11 us/run for
kv=1024,top_k=768 and 55.62 us/run for kv=2048,top_k=768. Direct sparse
attention on the same dk=576,dv=512,top_k=768 shapes measured
988.95 us/run and 984.50 us/run. That is roughly an 18x decode-path win
for compact selected-KV flash over direct sparse at the model-native top-k
width.
Short phase fixtures measured the opposite shape for direct sparse prefill:
dense masked flash stayed around 68.58-70.80 us/run for 4-16 token batches,
while direct sparse measured 461.98-473.75 us/run. That makes dense masked
flash roughly 6.5-6.9x faster on those short phase fixtures. This is why
glm-dsa-v1 keeps short_prefill: "dense" and verify: "auto" as the package
defaults, with direct sparse prefill reserved for explicit runtime/package
policy after backend evidence exists. Treat these as backend evidence for the
current threshold defaults, not as portable constants across every device or
quant.
Once those attention phase gates are in place, the measured local GLM-5.2
bottleneck shifts to MoE expert execution. The gated Metal MoE fixture
estimates a routed FFN decode layer at 391.13 us: routed gate/up/down matmuls
account for 375.42 us (96.0%), routed fused SwiGLU accounts for 5.35 us
(1.4%), and route/top-k plus weighted sum accounts for 10.36 us (2.6%).
The shared expert is equally important: a production-shaped fused GLU shared
expert plus final add measured 405.32 us, making the routed+shared FFN
estimate 796.45 us with the shared expert at 50.9%. These numbers justify
prioritizing backend MUL_MAT_ID/expert matmul and shared-expert work after
sparse-attention correctness, but they do not require a new manifest object.
Policy remains the semantic phase contract under generation.policy;
performance cutoffs and byte/token limits remain numeric resolver inputs under
generation.thresholds.
A component breakdown also corrected a bad diagnostic path: the unfused
silu(gate) * up whole-graph row measured 291.73 us, but the normal
llama.cpp shared expert path already uses ggml_swiglu_split(). The fused
SwiGLU split row measured only 4.09 us (8.24 us including final add), so
custom activation fusion is not the next local target. Treat the fused GLU
numbers as the production-shaped evidence and keep optimization pressure on
routed q2/q3 MUL_MAT_ID, q2_K down quality experiments, and deeper expert
matmul/layout work.
The combined FFN fixture is the current Phase E decision gate because it keeps
routed and shared expert execution in one production-shaped graph. With
n_embd=6144, the q3_K routed down baseline measured 1147.21 us; changing
decoder routed down projections to q2_K measured 817.38 us (1.40x faster),
and keeping q3_K routed down while using merged shared gate/up measured
868.59 us (1.32x faster). Route/top-k plus weights measured only 3.26 us,
weighted sum measured 6.69 us, and routed fused SwiGLU measured 5.09 us, so
the next practical levers are whole-graph expert matmul/layout work and a
quality-tested q2_K routed-down quant recipe. The q2_K routed-down recipe must
exclude blk.78, the native NextN/MTP block, until quality and speculation
tests prove that lowering the MTP block is acceptable. Treat these as
optimization-priority evidence, not as a new model-family schema branch:
quality-bearing quant changes still need separate evaluation under the normal
benchmark flow.
The Phase E report can be run with GLM52_PHASE_E_REQUIRE_GATES=1 to fail
loudly when production-shaped MoE evidence is missing. With
GLM52_PHASE_E_KERNEL_SWEEP=1, the same gate also requires the dispatch-policy
sweep rows. Forcing one-token q3_K routed down through Metal mul_mm_id
measured 860.96 us, compared with 164.46 us on the default mul_mv_id
path. q3_K mul_mv_id simdgroup tuning measured 164.46 us at the default
nsg=2 and 163.92 us for the best sampled row, which is noise-level rather
than a real policy win. The row-height sweep kept that conclusion intact:
default nr0=4 measured 163.98 us, which was also the best sampled row;
nr0=1 and nr0=2 were both around 195-196 us, and nr0=8 was 166.20 us.
A fixed k=2048 q3_K GLM-down specialization measured 164.04 us at default
nsg=2 and 162.67 us at nsg=4, only 1.01x faster than ordinary default
q3_K mul_mv_id. Keep q3_K down work focused on a deeper expert matmul/layout
specialization or a quality-tested q2_K down alternative, not generic
matrix-matrix cutoff, simdgroup, row-height, or fixed-block knobs.
Production-shaped routed whole-graph consumer rows remain useful as harness
history and as a guard against timing only a graph tail. They fixed an earlier
perf-mode issue where the fixture had run_whole_graph() for correctness but
timed only the final GGML_OP_MOE_WEIGHTED_SUM during perf. The newer combined
FFN fixture supersedes those rows as the policy decision gate because it keeps
routed and shared experts together. Keep
generation.policy.experimental.moe_weighted_down at evidence-gated; it is
not a package default. The q2_K down alternative is still the useful follow-up
because it produced the clearest combined-graph speedup, but it needs quality
validation before it can become a model-package quant policy.
The IndexShare numbers are why generation.policy.indexshare is a first-class
policy field instead of an implementation note. For GLM-5.2-style DSA with a
top_k width of 768, carrying the shared top-k sideband costs 3 KiB/token.
That is 384 KiB for a 128-token chunk, 1.5 MiB for 512 tokens, 6 MiB for
2048 tokens, and 384 MiB for a full 128k-token window. By comparison, a dense
float sparse mask over 128k visible KV costs 512 KiB/token: 64 MiB for 128
tokens, 256 MiB for 512 tokens, and 1 GiB for 2048 tokens. IndexShare still
has real bandwidth cost, but it is dramatically smaller than dense mask
materialization and preserves the model's Full-layer routing decision for
Shared layers.
Generation policy consumers should therefore treat the GLM-DSA policy as a phase-aware resolver contract:
- Decode starts with
compact-flashwhen backend support exists, because the compact selected-row path wins on measured one-token and post-prefill decode shapes. - Short prefill starts with
dense, because measured sparse/indexer overhead is worse below the package threshold. - Long prefill starts with
sparse-chunked, because dense masks become memory-hostile as visible KV grows. - Verification starts with
autountil verifier-specific sparse parity and performance are measured. - IndexShare is
required, because Shared layers should consume the model's cached top-k decision rather than recomputing silently.
When present, generation may also declare speculative_decoding defaults:
default: the strategy id the package recommends for this distribution.proposers: a map of reusable proposal-source ids to configuration.strategies: a map of strategy id to strategy configuration.
This object is a sibling of policy and thresholds. It is not an
experimental policy field, because speculation can be disabled, forced, or
selected independently from the attention/MoE phase resolver.
The current native MTP strategy shape is:
{
"type": "native-mtp",
"prediction_depth": 1,
"layer_indices": [47],
"window_policy": {
"default": "fixed",
"initial_window": 1,
"min_window": 1,
"max_window": 1,
"pipeline_depth": 1
}
}New packages SHOULD name the native MTP source under proposers and reference
it from the strategy. This also permits a package to add an N-gram sidecar
without changing the MTP source:
{
"default": "mtp-cache",
"proposers": {
"mtp": {
"type": "native-mtp",
"prediction_depth": 1,
"layer_indices": [47]
},
"cache": {
"type": "ngram-cache",
"ngram_min": 2,
"ngram_max": 4,
"max_proposal_tokens": 10,
"history_scope": "request"
}
},
"strategies": {
"mtp-cache": {
"type": "composite",
"primary": "mtp",
"extender": "cache",
"extension_policy": {
"max_tokens": 8
}
}
}
}Supported proposer types are native-mtp, ngram-cache, and ngram-suffix.
Cache and suffix proposers MUST use history_scope: "request".
An ngram-cache proposer MUST use ngram_max no greater than 4, llama.cpp's
current cache match-window limit. An ngram-suffix proposer MUST satisfy
3 <= ngram_min <= ngram_max <= 64. Both contain only target-committed history
for one request and are never shared between users or sessions. A composite
strategy MUST use a native-mtp primary and an N-gram extender. Its
extension_policy bounds the adaptive tail; every combined candidate is still
verified by one target VerifyWindow.
The package schema separates a proposer match length from its output budget:
| Field | Applies to | Requirement |
|---|---|---|
prediction_depth |
native-mtp |
Must be 1 for the current native MTP runtime. |
layer_indices |
native-mtp |
Must identify the package layers that contain the model's NextN/MTP tensors. |
ngram_min / ngram_max |
N-gram proposers | Define the historical token match range. Both are required and ngram_min <= ngram_max. |
max_proposal_tokens |
N-gram proposers | Caps how many continuation tokens the proposer may return. It is independent of ngram_max. |
history_scope |
ngram-cache, ngram-suffix |
Must be "request"; a history proposer never observes another request's tokens. |
window_policy.pipeline_depth |
All strategies | Optional positive per-request capacity for in-flight verification windows. Omission preserves the legacy depth of 1; package defaults above 1 require topology/workload-specific evidence. |
initial_tokens / max_tokens |
composite extension policy | Bound the adaptive N-gram tail after an MTP prefix. |
tail_backoff_proposals |
composite extension policy | Sets how many proposals to back off after an unhelpful tail. |
ngram-cache is a request-local incremental lookup that starts after a
provisional MTP prefix; its
match window is intentionally capped at four tokens by the current llama.cpp
ABI. ngram-suffix uses a request-local exact-seed index to select the longest
earlier suffix, up to 64 tokens, and can also start after a provisional MTP
prefix. No N-gram proposer is authoritative: the target verifies the proposal
and commits the accepted prefix only.
Packages that expose the full product menu SHOULD use stable strategy ids:
mtp, ngram-cache, ngram-suffix, mtp-cache, and mtp-suffix. mtp may
reference a reusable native-mtp proposer instead
of repeating its prediction-depth and layer metadata. The N-gram-only names
use proposer; the composite names use that MTP proposer as primary and the
corresponding N-gram proposer as extender. disabled is a runtime/operator
baseline, not a package strategy.
Native MTP strategy rules:
typeMUST benative-mtp.prediction_depthMUST be1for the current Skippy native MTP path.layer_indicesMUST list package layer indices containing native MTP/NextN tensors, usually the finalblk.N.nextn.*block emitted by GLM GGUF conversion.window_policySHOULD be fixed to1until runtimes support wider native MTP heads.- package writers MUST NOT advertise a native MTP default unless validation has found the required native prediction tensors in the selected package artifacts.
Draft-model speculation may use the same strategy map with type: "draft-model" and fields such as draft_model and adaptive window_policy.
Consumers that do not recognize a strategy type MUST ignore it unless it is the
declared default for a request they are trying to serve.
Operators may override the package recommendation in config.toml with
speculative.strategy, or for one mesh-llm serve invocation with
--speculative-strategy. auto uses the package/runtime default, mtp forces
the direct native-MTP control, and disabled disables speculation. A named
package strategy such as mtp-cache is accepted only when the selected package
declares it. Precedence is CLI invocation, selected model entry, then global
defaults. These layers may bound N-gram proposal size, extension depth,
cooldown, and VerifyWindow depth. A named package strategy cannot be invented
outside its package. For direct GGUF operation, operators may explicitly select
the request-local ngram-cache or ngram-suffix proposer by supplying valid
N-gram bounds; mesh-llm constructs
and validates that generic plan before starting Skippy. skippy-server receives
the resulting typed plan and does not repeat this policy resolution.
Operators may also pass the legacy native-mtp-n1 value; the runtime normalizes it to mtp for backward compatibility. New configs should use mtp.
See Speculative Decode Configuration
for the operator-side config.toml controls and CLI equivalents. Package
authors should publish conservative, tested values in the manifest; operators
can use configuration to choose a strategy or tighten its bounds without
changing the package's declared topology.
For a stage with layer_start..layer_end, consumers select:
shared.metadatashared.embeddingsonly when the stage owns the input boundary- every
layers[]entry withlayer_indexinlayer_start..layer_end shared.outputonly when the stage owns the final output boundary
The selected parts are sufficient to load the stage. A normal package-backed runtime SHOULD load selected parts directly. If a runtime composes those parts into a per-stage GGUF file, that file is derived cache and MUST NOT become the published repository format.
Projectors are selected independently from layer parts. For a package-backed multimodal model:
- an explicit stage
projector_pathwins; - otherwise, stage 0 or a single-stage runtime uses the first declared
projectors[]entry withkind: "mmproj"; - downstream stages do not load projector artifacts.
Consumers MUST NOT infer projector identity from sibling files or filename stems. If a package needs a projector, the manifest must declare it explicitly.
Package creation SHOULD use:
skippy-model-package write-package org/repo:distribution --out-dir model-package/Multimodal packages SHOULD declare projector artifacts at write time:
skippy-model-package write-package org/repo:distribution \
--projector mmproj-model-f16.gguf \
--out-dir model-package/The package writer copies declared projectors into projectors/, records their
checksums and sizes in model-package.json, and keeps them as durable package
artifacts.
Local source GGUF paths are allowed only with explicit provenance:
skippy-model-package write-package ./model.gguf \
--out-dir model-package/ \
--model-id org/repo:distribution \
--source-revision <commit> \
--source-file model.ggufBefore publishing, run package validation against the source model:
skippy-model-package validate-package /path/to/source.gguf model-package/A published repository SHOULD include a short README.md with:
- source model coordinate and revision;
- source artifact filename and checksum;
- package manifest checksum;
- projector artifact filenames and checksums, when present;
- layer count and activation width;
- skippy ABI version used to write the package;
- package generation defaults such as native MTP strategy id and prediction depth, when declared;
- validation command and result;
- any model-family certification notes, such as exact-cache policy or required activation sidebands.
Before a stage starts, consumers MUST validate:
model-package.jsonparses as schema version1;formatislayer-package;skippy_abi_versionis compatible with the runtime ABI;model_idand source identity fields are non-empty;- requested layer range is non-empty and within
layer_count; - requested layers exist and are not duplicated in the manifest;
- selected artifact paths are relative, safe, and files;
- selected artifact sizes match
artifact_bytes; - declared projector paths are relative, safe, and files;
- declared projector sizes match
artifact_bytes.
Consumers MUST verify SHA-256 checksums for selected artifacts before using an
hf:// layer package, including cache-hit resolutions. Local package
directories MAY keep checksum verification behind SKIPPY_VERIFY_PACKAGE_SHA
for development workflows.
Metadata-only inspection for inventory, identity discovery, or prepare planning MAY validate only the manifest and shared metadata artifact, and must not require a non-empty stage layer range. A real stage load still requires the non-empty range and selected-artifact checks above.
Implementations MAY cache successful checksum verification results. Cache keys and records should be derived from manifest and artifact identity plus file metadata, and should not store raw local package paths.
Checksum verification SHOULD include declared projectors when the package is first downloaded, when the package is validated by tooling, or when a stage is about to use a projector.
Manifest schema changes MUST be additive when possible. New optional fields may
be added to schema version 1 if old runtimes can ignore them safely.
projectors is an additive schema-version-1 field. Old packages without it
remain valid. Runtimes that do not understand projectors may still serve text
stages from the package, but they MUST reject multimodal serving requests that
require an undeclared or unsupported projector.
Changes that alter tensor ownership, layer indexing, path semantics, ABI
compatibility, or required fields MUST use a new schema version or a new
format value. Runtimes MUST reject unknown schema versions and incompatible
ABI versions rather than attempting best-effort loading.
Package repositories do not change mesh gossip or wire protocol semantics by themselves. When package metadata is advertised through mesh status or planning, new fields must follow the normal additive compatibility rules for mesh protocol data.
- Whether package repos should include an optional signed manifest or checksum sidecar for deployments that do not trust the hosting backend.
- Whether large repositories should publish one branch/tag per source revision and quantization, or one immutable repository per distribution.
- Whether package validation results should be machine-readable artifacts in
the repo or remain documented in
README.md.