Task guide

August 11, 2026 · View on GitHub

How to port a model, kernel, or feature. The rules are in AGENTS.md; this is the method. Nothing here is binding on its own.

Before you write anything

Verify the recorded gap against the current pinned upstream and the current local head — not against the spec's description of them, which may be stale. Check git log -S for the symbol and read the row's spec; a surprising amount of "missing" work already landed.

Enumerate the complete upstream surface before choosing a slice: every mode, default, dtype, error path, and edge case, plus the upstream tests that cover them. Classify each one as ported, deliberately deferred, or not applicable. A slice chosen before this enumeration almost always cuts through the middle of a mode.

Commit the spike spec with source and dependency anchors. Then implement.

The port cycle

Implement the smallest coherent vertical slice — something that runs end-to-end, not a layer that nothing calls yet. Keep local names and structure mechanically traceable to upstream so the next person can diff them by eye. Record every C++ adaptation you had to make and why.

Port the upstream test before the behavior. Capture the red. Implement. Get focused green. Run the full gate. Then hand to a fresh reviewer.

Advance the parity pin only after every affected row and gate is reconciled.

Grounding a claim

Cite the file:line you ported from. When a number is involved, read the whole executing chain rather than the top-level Python: FlashInfer, CUTLASS, cuBLASLt, DeepGEMM, torch/Inductor, generated code, and the local dispatch path all decide what actually ran. Dump the generated kernel before concluding a lever is unreachable.

Anything genuinely written from scratch is recorded as such in porting-inventory.md §9. "I couldn't find it upstream" is a search result, not a conclusion — say which paths you searched.

Mirror the memory format, not just the math

A token-exactness gate cannot catch a dtype that is too WIDE. F32 where upstream uses bf16 is more precise: tokens still match, SACRED still passes, and we quietly move twice the bytes. Every correctness gate we own is blind to it. So it has to be checked deliberately, once per ported path:

AskWhere upstream answers it
What dtype does the linear method OUTPUT?the quant method's apply/out_dtype (e.g. ModelOpt fp8 uses torch.get_default_dtype() = bf16)
What kv_cache_dtype is RESOLVED for this checkpoint?it is not always the CLI default — vLLM derives it from kv_cache_quant_algo in the checkpoint's quantization_config
What dtype do the intermediate activation buffers carry?read the consumer, not the producer: a buffer is only as narrow as whoever reads it
Is a projection one physical GEMM or several?merged linears (QKVParallelLinear, MergedColumnParallelLinear) are one, and upstream may requantize mismatched shards rather than decline to merge

Record the answers in the row's spec. If we deliberately diverge — a wider accumulate for a reason — say so and say what it costs in bytes per token.

Confirm the resolved config at RUNTIME, not from source. Upstream logs its resolved engine config on startup; read it. This project has repeatedly had a confident source reading contradicted by a runtime log — a kernel name read as a fusion, a code path read as selected when a capability predicate excluded it, a cache dtype read as the CLI default when the checkpoint overrode it. Source inspection establishes candidates; the running engine establishes what ran.

Why this section exists: a dtype divergence on the largest activation buffers is a per-token cost. It is therefore invisible at batch 1 with a short prompt, invisible to token gates, and shows up only as a flat throughput deficit that does not shrink with concurrency — the hardest signature to attribute after the fact.

Shared seams

Route through the shared path or record one exact tracked exception:

BehaviorSeam
add + RMSNorm and friendsvt::FusedChain
mergeable MLP projectionslayers::MlpGateUpMethodBase, vt::MergedGemmGroup
decodeModelRegistry::Forward, dense_attn::AttnBlock, on-device sampling
anything a user can reachinclude/vllm.h

A capability reachable only through an example's internals is not shipped. Grow the ABI first, then rewrite the example as a thin client, then delete the parallel implementation.

New hardware and new models are additive files mirroring vLLM's structure — not edits that special-case an existing path.

Where things live

The pinned upstream revision and current parity state are in upstream-sync.md and the owning matrices. The parity ledger records each introduced change against its upstream reference. Matrices hold current coverage. Do not restate any of it here.