This file provides guidance to coding agents (Claude Code, OpenCode) when working with code in this repository.
A PowerShell wrapper around upstream llama.cpp, pinned as a submodule at vendor/llama.cpp/. No original C/C++ lives here — only .ps1 scripts driving CMake + MSVC + Conda. The session shell is bash on Windows; the project's own scripts must run from pwsh/powershell.
./rebuild_llama.cpp.ps1 # auto-detects CUDA vs OpenBLAS
./rebuild_llama.cpp.ps1 -version "b1138" # pin a tag / commit
./rebuild_llama.cpp.ps1 -pullRequest "18675" # build a PR
./rebuild_llama.cpp.ps1 -target "llama-server" # CMake target subset
./rebuild_llama.cpp.ps1 -blasAccelerator OFF # OpenBLAS | CUDA | OFF
./examples/server.ps1 -model ".\vendor\llama.cpp\models\<x>.gguf"
Get-Help -Detailed ./examples/server.ps1 # full option listBinaries land in ./vendor/llama.cpp/build/bin/Release/. Conda env llama.cpp (Python 3.12) must already exist — the scripts call conda activate llama.cpp themselves.
No tests, no linter. Verify changes by running an example script against a real GGUF model.
- The submodule always shows dirty.
rebuild_llama.cpp.ps1prepends an OpenBLAS linking shim tovendor/llama.cpp/CMakeLists.txt(idempotent; workaround forfind_package(BLAS)failing on Windows)..gitmodulessetsignore = dirtyfor this reason — don't "clean it up." - Each build wipes the submodule back to
origin/masterthen checks out the requested-version/ PR. Any local edits undervendor/llama.cpp/are lost by design. The reset/--remotestep is scoped tovendor/llama.cpponly — other submodules (e.g.vendor/Qwen-Fixed-Chat-Templates, default branchmain) stay at the SHA pinned in the superproject and are never advanced by the build script. To bump them, do it manually:git -C vendor/Qwen-Fixed-Chat-Templates fetch && git -C vendor/Qwen-Fixed-Chat-Templates checkout <sha> && git add vendor/Qwen-Fixed-Chat-Templates && git commit. Once the pin is committed, the nextrebuild_llama.cpp.ps1mirrors it into the working tree (auto-discovered from.gitmodules,--force); hand-edits inside the submodule do not survive a rebuild. ml64.exe(MASM) must be passed as-DCMAKE_ASM_COMPILER. Upstreamggml/CMakeLists.txtsetscmake_policy(SET CMP0194 NEW)and declaresproject(... ASM); on CMake 4.1+ with the VS generator this rejectscl.exeas the ASM compiler. The script locatesml64.exeviavswhere.exe. Don't remove. Thevswherecall passes-requires Microsoft.VisualStudio.Component.VC.Tools.x86.x64alongside-latest— this is deliberate, not redundant:-latestalone returns the newest-installed instance by timestamp, which on a machine with multiple instances may be a Build Tools install lacking the C++ workload, so-findreturns nothing and the build throws "ml64.exe not found" even though another instance (e.g. Community) has it (#3).-requiresnarrows-latestto instances that actually carry the MSVC x64 toolset, matching the pattern upstream uses in.github/workflows/build-cpu.yml. Don't drop the-requiresfilter.- CUDA is selected iff both
nvidia-smiandnvccare on PATH. Missing either silently falls back to OpenBLAS. - CUDA builds pass
-DGGML_CUDA_FA_ALL_QUANTS=ON. Without it the CUDA flash-attention path compiles only four symmetric KV kernels —f16/f16,q4_0/q4_0,q8_0/q8_0,bf16/bf16(vendor/llama.cpp/ggml/src/ggml-cuda/CMakeLists.txt:119-124) — and the dispatcher returnsBEST_FATTN_KERNEL_NONE->GGML_ABORTfor any other K type or any mismatched K/V pair (ggml-cuda/fattn.cu:424-446). The flag pulls in the fullfattn-vec*.cuset so asymmetric / q5 / q4_1 caches work (the presets useq5_0K +q4_1V). Costs extranvcccompile time. - Build parallelism is SMT-aware.
cmake --build --parallelis fed a count derived fromWin32_Processor. Upstream'sUseMultiToolTask=true+EnforceProcessCountAcrossBuilds=true(vendor/llama.cpp/CMakeLists.txt:92-93) makes this the single cap on concurrentcl.exe/nvcc— no per-project/MPmultiplication. On SMT CPUs it uses physical cores (Sum(NumberOfCores)): dropping the logical siblings avoids starving the scheduler / ~doubling peaknvccRAM (no throughput gain) and leaves them free so the machine stays usable. On non-SMT CPUs where physical == logical (hybrid Arrow/Lunar Lake; e.g. Core Ultra 9 285HX = 8P+16E, 24 threads) using all cores would peg the box at 100%, so it backs off to 80% of physical (floor(cores * 0.8)= 19 on the 285HX) to keep the machine usable during builds. Override with-parallelJobs N. requirements_override.txtlayers on top of upstreamvendor/llama.cpp/requirements.txt. It pinstorchto a CUDA 12.6 wheel, addstiktoken(missing upstream, required for GLM), pinstransformers==5.3.0, and narrowsnumpyto resolve anopencv-python-headlessconflict. When bumping any of these, verify both constraints still hold.server.ps1reads GGUF metadata by shelling out tovendor/llama.cpp/gguf-py/gguf/scripts/gguf_dump.py. Upstream has moved this path before (CHANGELOG 1.24.0) — if server startup fails with "Failed to extract model details", check the path first.server.ps1 -additionalArgumentssplits on whitespace and re-pairs tokens into key/value flags. Values that contain spaces will not survive this parser.speed-bench.ps1drives a router-mode server, not a single model — it shells out to the vendoredvendor/llama.cpp/tools/server/bench/speed-bench/speed_bench.py(wiped/refreshed each rebuild, so it tracks the built binary) and sweeps the-modelspreset ids in order, pre-warming each via the router-only/models/loadendpoint and lazy-swapping through--models-max 1. Comparison anchors on the first id; models that fail to load are excluded, not fatal. Needs thedatasetspackage (deliberately not in the main requirements) plus network access for thenvidia/SPEED-Benchdataset. The router-only/v1/modelsand/models/loadendpoints mean it does not work against a plain single-model server. If startup fails reading the script after a rebuild, check whether upstream movedtools/server/bench/speed-bench/(same failure mode as thegguf_dump.pynote above).- Rebuild aborts on running build-tree processes. Before any destructive op,
rebuild_llama.cpp.ps1checksGet-Processfor any EXE undervendor/llama.cpp/build/and throws with the PID list. Catches the forgot-to-stop-llama-server.execase. load-mode = diodoes not enable DirectIO on Windows — it only disables mmap. The Win32llama_file::implctor takesuse_direct_ioas[[maybe_unused]]and just callsggml_fopen(vendor/llama.cpp/src/llama-mmap.cpp:86-95);FILE_FLAG_NO_BUFFERINGis never set andread_alignment()stays 1 (:391), so the loader's async staging buffers are 4 x 1 MiB of pinned host memory instead of the 4 x 64 MiB the aligned path would use (src/llama-model-loader.cpp:1418,:1427).has_direct_io()nevertheless returns a hardcodedtrueon Windows (:173-175). Net effect ofdioon this platform: buffered reads, no mmap, and zero VRAM cost — it is never implicated in a CUDA OOM. Keep the key for the deprecation-warning reason documented under Presets, but do not reason about page-cache behaviour from it.
VRAM-tier presets: presets/models_16GB_VRAM.ini, presets/models_24GB_VRAM.ini,
presets/models_16GB_8GB_VRAM.ini (dual-GPU).
See presets/README.md for the user-facing quick-start; notes below are for editing.
-
Only
models_16GB_8GB_VRAM.inipins devices. It setssplit-mode,main-gpu, andtensor-splitin its[*]section; the other two tiers set none of the three, so on a host with more than one CUDA device llama.cpp's defaultsplit-mode = layerspreads every entry across all visible GPUs — the tier name is then a floor, not a cap. Pin withCUDA_VISIBLE_DEVICESbefore launch, not--device: in router mode each child's argv is rebuilt from the preset (inst.meta.update_args), so a parent--devicenever reaches the child, while the environment is copied into every child (tools/server/server-models.cpp:802). Pinning is a throughput decision, not only a memory one —Ternary-Bonsai-27B-Q2_g64.ggufmeasured 56.5 t/s tg and 1418 t/s pp on one 16 GB card versus 36.5 t/s and 998 t/s spread over a 16 GB plus an 8 GB card.CUDA_VISIBLE_DEVICESindices followCUDA_DEVICE_ORDER, which defaults toFASTEST_FIRSTand therefore does not matchnvidia-smiordering; pass a GPU UUID to be unambiguous. -
All entries use
load-mode = dio; never pairdirect-iowithno-mmapagain. Both spellings are deprecated, and they write the same mutually exclusive enum —--no-mmapsetsLLAMA_LOAD_MODE_NONE(common/arg.cpp:2594) while--direct-iosetsLLAMA_LOAD_MODE_DIRECT_IO(:2603) — so setting both means only whichever is parsed last wins. Which one that is is not the INI order:common/preset.cppemitsopt.args.back()while iterating an unordered map. It happened to resolve to DirectIO, but a container-order change would silently downgrade toNONE, dropping DirectIO and mmap for plain buffered reads.diois the single equivalent of the old pair and also removes two deprecation warnings per launch. Valid values arenone,mmap,mlock,mmap+mlock,dio(arg.cpp:2615-2619) — anything else throws at startup. -
Qwen-VL entries pin
image-min-tokens = 1024.clip.cpp:1500sets the per-image token limits to(8, 4096), so the default minimum is 8 tokens — 8192 px atmerge = 2/patch = 16— andclip.cpp:1502-1506warns on every load because upstream needs >= 1024 tokens (1024x1024 px) for grounding (#16842). The key raises a floor only: images already above 1024 tokens are unchanged, smaller ones get upscaled, which costs context and CLIP time on the CPU because the 16 GB tier setsno-mmproj-offload = true. Applies to theqwen3vl_mergerentries (Qwen3.6 and Ternary-Bonsai); gemma-4 uses a different projector and must not get this key. -
mmproj-offload = truefails silently at startup on a saturated GPU. CLIP's warmup compute buffer OOMs but the server keeps running — only image requests error at generation time. Setfalseon tiers where LLM + KV already saturate VRAM. -
All Qwen 3.6 entries pin
chat-template-file = vendor\Qwen-Fixed-Chat-Templates\chat_template.jinja. Required, not redundant withjinja = true—chat-template-filereplaces the GGUF-embedded template entirely (vendor/llama.cpp/common/arg.cpp:3142,params.chat_template = read_file(value)). The upstream embedded template has documented issues with tool calls, role handling,<think>block rendering, agentic loops, and llama.cpp KV-prefix cache stability; the vendored template fixes all of them (full list invendor/Qwen-Fixed-Chat-Templates/README.md). Since v19 the template is a single unified file covering both Qwen 3.5 and 3.6 variants (the oldqwen3.5/andqwen3.6/subdirectories now live underarchive/). The template adds a<|think_on|>/<|think_off|>toggle, and v19 defaultspreserve_thinkingtotrue(past<think>blocks are kept chronologically for 100% KV prefix cache stability and agentic reasoning continuity). To strip past<think>blocks instead, setchat-template-kwargs = {"preserve_thinking":false}— at the cost of a lower KV cache hit rate. Path is repo-relative, sollama-servermust be launched from the repo root —read_file()resolves against the process CWD, not the INI file's directory.Qwen3-Coder-Nextentries deliberately keep their GGUF-embedded template; froggeric's README only claims compatibility for Qwen 3.5 / 3.6 variants. -
All gemma-4 entries pin
chat-template-file = vendor\llama.cpp\models\templates\google-gemma-4-31B-it.jinja. This is Google's fixed official template as aligned by upstream (#21704) — the exact file upstream'stests/test-chat.cpplocks against the native gemma4 chat handler (vendor/llama.cpp/common/chat.cpp:1216), so parser and template always come from the same submodule commit (each rebuild resets the submodule to master, mirroring the built binary). GGUF-embedded templates from conversions predating Google's template fixes lack the{#- OpenAI Chat Completions:marker; llama.cpp then logs "detected an outdated gemma4 chat template" and rewrites messages via C++ compatibility workarounds (common/chat.cpp:2250-2258) — the pin avoids that path. One file covers the whole series (12B / 26B-A4B / 31B, incl.<|image|>/<|audio|>placeholders), andreasoning = onmaps to the template'senable_thinkingkwarg (common/arg.cpp:3167-3175), so nochat-template-kwargsare needed. Unlike the Qwen template, past<|channel>thoughtblocks are stripped from history by design — Gemma 4 is trained that way — so cross-turn KV-prefix invalidation is inherent (ctx-checkpointsmitigates); do not add a preserve-thinking hack. If startup fails reading the template after a rebuild, check whether upstream movedmodels/templates/(same failure mode as thegguf_dump.pynote above). -
Both Bonsai entries use the same
chat-template-filepin as the Qwen 3.6 entries.Ternary-Bonsai-27BandBonsai-27Bship from separate HF repos but are both Qwen3.6-27B derivatives: archqwen35, and their tokenizers are byte-identical to stock Qwen3.6-27B (248320 tokens, same merges,eos = 248046) right down to the same 7764-byte embedded template — which is exactly the upstream template the pin exists to replace.general.sampling.temp = 1.0is embedded in both GGUFs and applied atcommon/common.cpp:1194, sotemphas to be pinned in the preset or generation runs at 1.0. The presets use0.6to match the sibling Qwen 3.6 entries; Prism's own card benchmarks at0.7. Unlike the DSpark sidecar below, both weight files are mainline-packed (Q2_0atQK2_0 64,Q1_0atQK1_0 128) and load without a tensor-offset mismatch. -
The DSpark drafter shipped beside Ternary Bonsai 27B cannot be enabled on mainline.
Ternary-Bonsai-27B-dspark-Q4_1.ggufhas itstoken_embd.weightinQ2_0at Prism's group-128 packing while mainline is group-64 (QK2_0 64,ggml/src/ggml-common.h), sogguf_init_from_readerrejects the file on a tensor-offset mismatch before any architecture dispatch. Repacking would not help:general.architecture = 'dspark'is unregistered (src/llama-arch.cpp:136has onlydflash), and mainline's DSpark is DeepSeek-V4 DFlash + Markov (src/models/dflash.cpp, tensorsmarkov_w1/markov_w2/conf_proj, requiring MLA and sqrtsoftplus MoE scoring), not Prism's 6-layer Qwen3.6-shaped drafter (dspark.fc,dspark.log_snr_fc*,dspark.markov_head_*). Upstream confirmed on #25707 that it stays fork-only. The GGUF carries no MTP tensors either, sodraft-mtpis out and the entry usesngram-mod. Only the group-64 pack is mainline-loadable —Q2_0.ggufandPQ2_0.ggufin the same HF repo are group-128 fork packs. -
DeepSeek-V4-Flash-0731 must set
cache-type-kandcache-type-vto the same value. Arch isdeepseek4(the HF card's "dflash / 20B" is the DSpark sidecar's metadata, not the model).llama-context.cpp:3560-3563compares the two values and refuses to create the context —does not support different K (%s) and V (%s) cache types— forLLM_ARCH_DEEPSEEK4specifically, becausehparams.is_mla()is false for this arch and the guard needs the explicit disjunct. Socache-type-v = q8_0is load-bearing even though V is never allocated: DSV4 is K-only everywhere (dsv4_make_k_only()atllama-kv-cache-dsv4.cpp:831-835forcesis_mlatrue on hparams copies, sohas_v = !is_mlaatllama-kv-cache.cpp:229is false). Copying the Qwen dual-GPU pairq5_0K /q4_1V here is startup-fatal, not merely wasteful.q8_0also clearsn_embd_head_k() % 64 == 0so quantized K gets the Hadamard rotation (llama-kv-cache.cpp:319-323); the lightning-indexer cache is rotated unconditionally for this arch (:325-329).kv-unifiedis silently discarded (GGML_UNUSED(unified),dsv4.cpp:1189-1192), andcache-type-*-draftis dead without a draft model. -
The DSV4 KV cache is tiny, so context is cheap and quantizing it buys little. 43 layers split 2 raw / 21 CSA (ratio 4) / 20 HCA (ratio 128) via
attention.compress_ratios, all K-only atn_embd_k_gqa = 512; the raw tier is SWA-windowed toPAD(min(n_ctx, 128 + n_ubatch), 256)= 768 cells regardless ofctx-size. At 262144 that is 942 MiB atq8_0(1764 MiB at f16) plus a fixed 11.64 MiB of F32 compressor state that no cache type shrinks. Never setswa-full: it collapses the window formula ton_ctx(llama-kv-cache-iswa.cpp:76-81), turning a 17 MiB raw cache into ~11 GiB. The server warnsswa_full is not supportedonly after the cache is built, so the flag still takes effect. -
Leave
fit = onand never addn-cpu-moe/-otto the DeepSeek entry. Measured UD-Q8_K_XL composition: 137.06 GiB routed experts (MXFP4, 90.9%), 2.02 GiB shared experts, 11.67 GiB non-expert — so non-expert + shared is only 13.69 GiB and fits a 24 GB card alongside the KV with room for a full expert layer or two (3.19 GiB each).fitfinds that split at sub-layer granularity (fit.cpp:399-441,:719-769); forcing-ncmoe 43would push all experts to CPU and strand ~9 GiB of VRAM. Any user-ot/--cpu-moe/--n-cpu-moeaborts fit outright (fit.cpp:395-397), as does settingn-gpu-layersto anything but-1(fit.cpp:374-376) — which is why the entry keeps-1explicitly. Note--cpu-moe's pattern matches only_exps/_chexps, so shared experts would stay on GPU either way. -
no-host = trueis mandatory on the DeepSeek entry, and this is the trap that actually stops it loading. Unlessno_hostis set,make_cpu_buft_list()prependsggml_backend_dev_host_buffer_type()to the CPU buffer list, so every CPU-resident tensor is allocated in aCUDA_Host(page-locked) buffer (src/llama-model.cpp:896-917, wired fromparams.no_hostatcommon/common.cpp:1611andinclude/llama.h:338). For this model the loader then reports oneCUDA_Host model buffer size = 137046.96 MiB— a 133.8 GiBcudaMallocHoston a 192 GB box. The reservation succeeds, soggml_cuda_host_malloc's clean-failure fallback to an ordinary CPU buffer never fires; the failure happens later while the pages are committed during the read and surfaces asCUDA error: out of memoryinsidecudaEventSynchronizeatsrc/llama-model-loader.cpp:1591. That makes a host-memory problem look like a VRAM problem — raisingfit-targetdoes not help it, and neither does changingload-mode. Withno-host = truethe same config loads in ~144 s at 18650 MiB VRAM and ~124 GiB of ordinary host RAM.GGML_CUDA_NO_PINNED=1is the env-var equivalent. This applies to any entry that pushes tens of GiB of experts to CPU, not only DeepSeek. The GPU upload staging buffers are unaffected — the loader asks for those buffer types directly (src/llama-model-loader.cpp:1467) rather than throughcpu_buft_list— but expert weights thatop-offloadships to the GPU for large-batch matmuls now come from pageable memory, which may cost some prompt-processing throughput. There is no way to keep that and still load. -
fit-target = 3072on the DeepSeek entry is a WDDM safety margin, not the fix for the load failure (that isno-hostabove).fitmeasures rather than guesses — it performs ano_allocmodel load plus a real graph reservation (fit.cpp:56-75), so its KV figure is byte-exact (942 MiB at 262144/q8_0) and its compute figure is a genuineggml_gallocrmeasurement. What it cannot see is the CUDA VMM scratch pool (32 GiB of VA reserved, physical pages committed on demand,ggml/src/ggml-cuda/ggml-cuda.cu:536-656), the lazy cuBLAS workspace, and CUDA graph instances; none are reported tomemory_breakdown(). It also takes a singlecudaMemGetInfosnapshot at t=0 (fit.cpp:194) and carries no WDDM or framebuffer allowance anywhere. At the default 1024 MiB margin fit keeps blk.0 and blk.1 routed experts on the GPU (6.375 GiB — every one of the 43 layers carries 3.188 GiB of routed experts, there are no dense layers) and leaves only 1368 of 23139 usable MiB for those untracked consumers.3072leaves 3497 MiB and costs one extra expert layer on CPU (~2.3% more expert traffic). Do not raise it to 6144: that collapses-nglto 38 and starts stranding whole layers.--fit-targetwrites onlyparams.fit_params_targetand nevermparams, so unlike-ngl/-ncmoe/-otit cannot trip the aborts atfit.cpp:374-397. -
cache-ramis 16384 on the DeepSeek entry, not the 51200 used elsewhere.fitreports 133.8 GiB ofHost modelweights for this entry (measured atfit-target = 3072), so on a 192 GB box a 50 GiB prompt cache overcommits and pages. A full-context prompt state at 262144/q8_0is 931 MiB (server-task.cpp:1671-1683— an entry larger than the whole limit is silently skipped), so 16 GiB still holds ~17 of them. Context checkpoints are separate and cheap: 14.5 MiB each and independent ofctx-size, because a DSV4 checkpoint stores only the 128-position SWA window plus the fixed compressor state. -
The DeepSeek-V4-Flash entry deliberately does not pin
chat-template-file. This is the one exception to the convention above. The GGUF ships Unsloth's fixed template, and both it and upstream's bundledmodels/templates/deepseek-ai-DeepSeek-V4-Flash-0731.jinja(#26398) satisfy the detection heuristic atcommon/chat.cpp:3170-3179(dsml_token+DSML+tool_calls), so both route to the native PEG parser (common_chat_params_init_deepseek_v3_2,chat.cpp:2097) and classify as V4 via thefunction_calls-absent test at:2105. Unsloth's additionally restoresreasoning_contenton tool calls, which the official template drops. Unlike gemma-4 there is no outdated-template rewrite path fordeepseek4— detection is all-or-nothing, and a miss degrades to the generic autoparser rather than being repaired.reasoning_efforthas no CLI flag (server-common.cpp:1089-1095honours only the literal"none"), so the only route ischat-template-kwargs = {"reasoning_effort":"high"}(or"max"); left unset the template defaults it tononeand emits no effort block at all, andreasoning = offvoids it entirely. Do not setreasoning-format: the compiled default is alreadydeepseek(common.h:631, despite the help text sayingauto), andnoneleaks</think>intocontent. -
Context shift and cache-reuse are permanently unavailable on
deepseek4.llama_kv_cache_dsv4::get_can_shift()returns false (dsv4.cpp:1394-1398), so the server force-disables both with a warning (server-context.cpp:1268-1278); slots then stop cleanly atSTOP_TYPE_LIMITinstead of shifting.seq_rmalso refuses partial removal whenn_rs_seq == 0(dsv4.cpp:1427-1429), whichngram-moddoes not set, so rollback goes through checkpoints — correct, but each rejected draft costs a ~14.5 MiB state restore and the net throughput effect is uncharacterised. Thedsparksidecar in the same HF repo is a genuine mainlinedflashdrafter (unlike the Bonsai one above), but is unusable here: its README requires--fit offplus full offload of target and drafter (11 GiB drafter + 13.69 GiB non-expert exceeds 24 GB),--spec-draft-n-maxis clamped to 5, multi-GPU needs a rebuild withGGML_SCHED_MAX_SPLIT_INPUTS=48, and it carries an open decode-time CUDA abort after ~2500 tokens (#26554). The regression that broke spec decoding on this arch (#26576, a 2Dwo_aindflash.cppafter #26531) is fixed by #26577 atb10269.
ngram-mod speculative decoding (--spec-type ngram-mod): model-agnostic, works on any model.
- All models:
spec-ngram-mod-n-match = 24,spec-ngram-mod-n-min = 48,spec-ngram-mod-n-max = 64(matches the struct defaults incommon/common.h:329-337and what--spec-defaultproduces atcommon/arg.cpp:4065-4074; ggerganov confirmed post-merge in PR #19164 that the min/max "likely don't need to be changed from the recommended values"; MoEs require long drafts and dense models tolerate them without noticeable cost). Flags were renamed from--draft-min/--draft-max/--spec-ngram-size-nin upstream PR #22397; the old names now error at startup. n_match < 16logs a "too small — poor quality is possible" warning atvendor/llama.cpp/common/speculative.cpp:1031-1034; parser accepts1..1024(common/arg.cpp:3606-3615), so 16 is the lowest non-warning value, not a hard floor. Min/max parsers accept0..1024(common/arg.cpp:3587-3605).- Memory overhead: ~16 MiB total, shared across all server slots
(single
common_ngram_modinstance allocated atcommon/speculative.cpp:1026). - Pool auto-resets on
begin()if occupancy > 25 %, and after 3 consecutive rounds with acceptance < 50 % (common/speculative.cpp:720-728,:790-806). Smallern_matchmakes these resets fire more often and wipes ngrams learned from the current prompt — another reason to stay atn_match ≥ 24.
- One bullet = one physical line. Never insert manual line-breaks; let the editor soft-wrap.
- Format:
- [Component] <verb> <thing>(Added / Changed / Fixed / Removed). - No rationale, no file paths, no line numbers, no explanatory prose. Rationale lives in AGENTS.md "Non-obvious behavior" or in the commit message.
- PR refs as bare
#NNNNN, at most once per release. - Canonical examples: [1.21.0] – [1.27.0] in CHANGELOG.md.
Non-committed agent artifacts (diffs, trace outputs, generated reports, experimental scripts) go under .tmp/sessions/<session-id>/ at the repo root; .tmp/ is gitignored. <session-id> is SESSION_ID when the platform injects it, otherwise a minted YYYYMMDD-HHMMSS-<random6>. Never write scratch files to .claude/, the repo root, or vendor/.