Causal LM + LoRA run prefill-only with a block-causal mask (shared state prefix, isolated question branches) and a
pointer readout over option boundary tokens, trained with log loss on public datasets plus generated policy and rule
records. No text generation. The released family is Qwen3.5 (0.8B / 4B / 9B) plus Kev-27B on the post-trained Qwen3.8-27B; kev-0.5b (Qwen2.5-0.5B, the prototype
this started as) and the Qwen3 generation are superseded but still published.
See README.md (deep dive), PLAN.md (where the research stands, what we have learned, the standing rules and what is next;
the full dated record of rounds 4-18 and everything before is frozen at git tag research-archive-2026-09-24),
docs/autoresearch.md (the operating program for an unattended research session) and docs/model-cards/ (one card per
checkpoint: recipe + metrics). README follows the Vercel Labs house style (tagline, for-the-badge badges, Highlights,
Title Case sections, API tables, Authors + License); model cards are formal.
- Env:
uv sync(add--extra servefor FastAPI + the TypeSafe SDK; on Apple Silicon it also pullsmlx-lmfor the MLX backend,--extra mlxalone for library use).kevis a real package (setuptools, installed editable byuv syncsince #14), Python 3.12 or 3.13 (requires-python = ">=3.12,<3.14"; 3.14 has no wheels for the pinned torch,.python-versionselects 3.13,uv sync --extra serve --python 3.13overrides explicitly),transformers>=5.17,<6,peft>=0.21,torch>=2.6,<2.9. The dev group carries pytest, matplotlib andmodal==1.5.5. - Train:
uv run python -m kev.train --suite evals/v7/decision-v7 --base Qwen/Qwen3.5-4B-Base --base_revision <sha> --epochs 2 --lr 5e-5 --batch 4 --accum 2 --dtype bf16 --checkpointing 1 --p_none_pair 0.25 --device cuda --out runs/kev-4b(the released 4B/9B recipe; 0.8B uses--lr 1e-4 --batch 8). Records come from--suite(its training partition),--data(your own JSONL, optionally+ --suite --replay N), or built on the fly from the public sources (--n_per_source, the smoke path).--helplists everything; the knobs that matter:- losses:
--perm_kl/--perm_frac(permutation KL),--ord_w(ranked probability score for Score),--anchor/--anchor_w/--anchor_sources(KL toward the frozen base's zero-shot answers, fromkev.anchors),--label_smoothing/--brier_w/--focal_gamma(the round-3 calibration screen; all default 0 and none is in a release). - augmentation / mix:
--p_none,--p_none_distract,--p_distract,--p_none_pair,--synthetic_repeat,--public_frac,--train_sources,--holdout.--none_pair_max_state N(plan keynone_pair_max_state, needs--p_none_pair > 0; absent = every record may pair, today's recipes byte for byte): only records whose state has at most N tokens (encode's count,<state>included) emit none pairs, drawn from each record's own stream (kev.train.none_pairs), and--length_sortcounts their two siblings (the state twice more) in the record's cost, so a pass cannot outgrow the cost its run was cut to. A pair repeats the whole state, so on long states it tripled a pass past the GPU (round 21's projection).--pass_tokens_max N(plan keypass_tokens_max, needs--length_sort 1and a hybrid base, whose passes are always rows or a shared prefix, whatpass_tokensmeasures (an attention-only base runs the packed mask, refused once the model is built); not with--row_budgetor--perm_kl; absent = today's plan byte for byte): the plan cuts each step on exact token shapes (kev.train.plan_shapes: every variant the epoch trains, siblings included, branches encoded without their state, the state counted once bystate_token_counts) instead of characters, and a step whose costliest pass is over N padded tokens (pass_tokens) gets one more micro-batch per rank until none is (every rank the same count; a record over N alone is refused before the first step). Round 21 ran out of memory at its 62nd step on a characters plan: a token-dense PII state set the padding of 16 states at 5,877 tokens, a 98.7k-token pass costed in characters like its slot's other passes of 33-50k tokens. 27B, 8 H200: N = 40,960 peaks at 101.6-108.2 GiB per GPU on the worst passes the plan allows (scripts/sft_probe.py --passes,runs/sft-probe/r22-ceiling-27b-8xh200). - architecture / precision:
--lora,--lora_targets all|dense|attn|qv(densefreezes the DeltaNet projections on hybrid bases),--head_dim,--option_isolation,--special_embeddings,--dtype(autocast) vs--weights_dtype(frozen backbone; bf16 is required by the fused MoE experts of 35B-A3B). - full-weight:
--full_ft 1 --weights_dtype bf16trains the whole text backbone (vision tower, LM head and MTP are never loaded) plus the head, withkev.full_ft.MasterAdamW(fp32 masters + moments; one GPU: in host memory, AutoJev's technique; undertorchrun: FSDP2 shards, state on the GPUs, the head replicated).--shared_prefix(default on with--full_ft 1; hybrid backbones) runs each record's state once and its question branches from it (kev.shared_prefix: the DeltaNet recurrent and conv state and the attention keys/values of the state, functional, one checkpointed step per layer), exact to the row form (tests/test_unit.py, tests/test_model.py).--length_sort 1deals each step's records into micro-batches of neighbours in length balanced by padded cost (--batchbecomes the average), so no rank waits long on another.--row_budget N(one GPU): padded tokens per forward/backward pass, a record that does not fit is split by question (27B on one H200 needs 8192; not with--perm_klor--anchor_w). Needs torch >= 2.8.--max_steps Nstops early. Resume:--save_every_minutes M/--save_every_steps Nwrite<out>/resume(fp32 masters + moments per rank, scheduler, RNG, data position; under torchrun from a host copy in a background thread, the interval stretched so blocking stays under 5 %),--resume 1continues bit for bit (same arguments and world size),--stop_after Nexits after a step. Snapshots:--snapshot_fractions 0.25,0.5,0.75(of the optimizer steps) /--snapshot_every_steps Nwrite loadable bf16 checkpoints into<snapshot_dir>/step-<N>/checkpoint(N zero-padded to 7 digits like resume points,step-0000389;--snapshot_dir, default<out>-snapshots;kev.full_ft.SnapshotWriter; at mostkev.budget.MAX_SNAPSHOTS= 8 per run, the disk they may take next to two resume points, refused at parse time or before the first step): the final checkpoint's files (save_pretrained shards +head.pt+ tokenizer;head.pt["snapshot"]= step, steps, epoch fraction, records seen) plussnapshot.json, written last, which marks it complete. Under torchrun the ranks gather into rank 0's host memory (the only blocking part) and rank 0 writes in a background thread; one GPU writes before going on. Snapshots are never deleted; a continued run skips complete ones and rewrites an incomplete one, and a resume point'slatest.jsonwaits for the snapshot being written, so no committed resume point passes a missing snapshot.training_metrics.jsonhas per-epoch gradient norms before clipping (grad_norm: mean, max, clipped steps), LoRA runs too; full-weight runs alsobackbone_save_seconds(the final gather + write) andsnapshots(blocking and write seconds per snapshot of that attempt; eachsnapshot.jsonhas its own). Trials: see Studies. 27B, 8 H200,--batch 8 --accum 2 --length_sort 1, records shaped like the SFT corpus (scripts/sft_probe.py --mix all): 7.6 records/s, one epoch of the corpus (192k records) ≈ 7.1 h / $293, 71 GB peak per GPU (runs/sft-probe/sft2-*, PR #125); one H200 (row form, PR #122) 0.84 records/s on ~1,000-token records. Long states (scripts/sft_probe.py --state_tokens 16384,32768,49152,65536 --questions 3,--batch 1 --accum 1 --length_sort 1, 8 H200,runs/sft-probe/lc-27b-8xh200): 1.12 / 2.33 / 3.79 / 5.45 s per record (14.8k-12.0k record tokens/s), 78 / 96 / 115 / 134 GiB peak per GPU of 140. A micro-batch whose states are unpadded runs the state through SDPA's causal flash kernel with no mask (kev.shared_prefix); with the explicit 64k x 64k mask a 64k step took 150 s instead of 44 and filled the GPU (runs/sft-probe/lc-27b-8xh200-mask-ab). - Only one training process at a time: two on MPS slow each other ~10x.
- losses:
- Smoke:
uv run python -m kev.train --n_per_source 40 --accum 4 --out runs/smoke(~1 min). - Benchmark (the eval path for everything current):
uv run python -m kev.benchmark --run <run dir | Hub id[@rev]> --suite evals/v4/transfer-v4 --out runs/<name>;--remote <url>scores any System One endpoint (--remote-concurrency Nkeeps N requests in flight; rows are identical),--data x.jsonlyour own labelled rows,--date_factsthe opt-in preprocessing,--allow-testis the only way to read a locked test. Writesrows.json(per question, with logits) +report.json(accuracy, ECE/Brier/NLL, selective coverage and AURC, permutation, isolation;environment: the kernel set the logits depend on, fromkev.predictors.kernel_environment: package versions, GPU, backbone dtype, attention implementation, the DeltaNet layer forward (kev.fused_qwen35 replaces it) and the conv and delta rule transformers bound; compare it before pairing two reads bit for bit).kev.calibrate --rows <rows.json>reports what one temperature fitted on those rows would do (raw / shipped / workload in-sample / workload group-disjoint OOF, paired bootstrap vs shipped; report only, writescalibration.jsonnext to the rows); external-suiterows.jsonare committed for this.kev.evaluateis the legacy prototype eval (runs/kev,eval.json) and is not used for releases.kev.compare --candidate <dir> --reference <dir>pairs two result dirs (record-clustered bootstrap).kev.jev --suite ... --out ...scores Jev through Vercel AI Gateway (AI SDKexperimental_evaluate, node worker inplayground/scripts/jev-evaluate.mjs; needsAI_GATEWAY_API_KEYor--provision-scope; budget-capped). - Studies:
kev.experiment --plan experiments/*.json --suite <suite> --out runs/<study>runs config-only trials (allowlist + ranges inexperiment.py: DEFAULTS/CHOICES/validated_trial, provenance (provenance.json, copied intoresult.json; itsenvironmentis the kernel set the trial's reads were scored on, report.json's block from the samekev.predictors.kernel_environment, taken from the loaded predictor and replaced when a resume re-scores; trials before it have none), coverage/isolation gates,results.jsonlledger,--transfer <suite>for an OOD read per trial,--aggregateto rank an existing directory,--resumefor interrupted trials (evaluation; unfinished full-weight trials continue training from their resume point),--wait-pidto queue behind a training job). A full-weight trial runs under torchrun on every GPU of its container, writes a resume point everyexperiment.RESUME_MINUTESand is continued after a timeout bykev.rounds watch, not by Modal (kev.budget:FULL_FT_RETRIEScontinuations, up to 24 h per attempt, $1,000 per study; the bound counts every attempt): trials spawn with Modal's retries off, because Modal charged each killed timed-out attempt twice and gave round 22's trial two of its three attempts (scripts/modal_retry_probe.py);runs/<study>.spawn.jsonis the attempt ledger (calls= each trial's current call,attempts= all of them), andmodal_app.py::resume --trial <label>(modal_app.continue_full_trial, whatwatchruns) spawns the next attempt only after a call ended by a timeout, within the ledger, with the study's GPU and timeout;--beyond-boundcontinues a trial without a ledger, uncounted. Each attempt is written to the ledger pending (a nonce) before its spawn; a pending entry blocks continuations until its call is adopted from the lease or it iskev.budget.LEASE_STALEold. The lease: every full-weight attempt holds<study>/<trial>/attempt.jsonon thekev-leasesvolume (modal_app.TrialLease: nonce, attempt, call id, heartbeat everyLEASE_HEARTBEATs from its own thread, marked ended on a clean exit); an attempt that finds another attempt's lease fresher thanLEASE_STALE(900 s) returns{"refused": ...}without touching the trial, andcontinue_full_trialwaits (bounded) for the old lease to end or go stale, so a continuation starts only after the old container is gone. It also keeps snapshots atexperiment.SNAPSHOT_FRACTIONS(0.25, 0.5, 0.75 of its optimizer steps) in<trial>/snapshots/step-<N>/checkpoint(plan keyssnapshot_fractions(a string,"none"for none) andsnapshot_every_steps, which needsmax_stepssovalidated_trialcan checkMAX_SNAPSHOTS; neither changes the config hash of a plan that omits it, nor the recipe). A 27B snapshot is ~51 GB of volume storage and never deleted: do not remove checkpoints or snapshots from the volume without asking. The volume copy is primary; a plan keysnapshot_hub_repo(off by default) also mirrors each committed snapshot and the final checkpoint to that PRIVATE Hub model repo (kev.mirror,modal_app.run_mirror: a CPU container spawned after the commit, HF token from the Modal secrethuggingface-secretorKEV_HF_SECRET; refuses a public repo, retries once, never fails training; the commit is recorded insnapshot.json["hub"]/checkpoint/hub.json, at<study>/<trial>/<step-N | final>/in the repo).modal_app.py::mirror_snapshots --study X [--paths /runs/...checkpoint] [--repo jaredpalmer/kev-snapshots] [--dry-run](re)uploads existing ones (a 27B checkpoint is ~51 GB; ask before mirroring those). The container commits the runs volume after every completed resume point and snapshot (a timeout skips the final commit); an attempt that fails with an error writesfailed.jsonand returns{"failed": ...}instead of raising, so it is never continued (kev.rounds.poll_modalreports it as a failure; a timeout it reports as"timeout").modal_app.py::pull(andkev.rounds watch) leaves full-weight shards (model*.safetensorsdirectly in acheckpoint/directory: final and snapshots) and resume points on the volume (--weightscopies them); read a snapshot like any checkpoint:::benchmarks --jobs "/runs/<study>/<trial>/snapshots/step-<N>/checkpoint@<suite>@<name>". - Rounds (every registered experiment since round 5): one spec per round,
experiments/rounds/r<N>.json, committed before any training or read: studies (plan, GPU, timeout, budget), arms (<size>-<label>: trial + parent), parents (trial + where each of its reads lives), read tags -> suites (--allow-testfor test panels,locked_testfor the locked read), the rule (panels of reads, bootstrapped metrics, criteria on paired bounds, rank) and confirmation stages.kev/rounds.pyis the one engine:uv run python -m kev.rounds {validate,launch,watch,launch-reads,readout,confirm} <spec>;watchpolls the spawned trials (state inruns/<study>.watch.json, resumable; DNS/connection errors retried, a trial's own exception is a failure, a timed-out full-weight trial gets its next attempt within the spawn record's ledger), pulls each finished trial's study (one pull per study at a time,modal_app.pull_lock) and launches its reads once (per-arm lock; the launch intent is written first toruns/r<N>-reads-<arm>.json, and an arm launched within its reads' timeout is not relaunched; a finished call that maps to no arm is logged and makeswatchexit non-zero), 60 s apart, then writesruns/r<N>-readout/round<N>.json;confirm <spec> --stage <s>writesruns/r<N>-verdict/<size>-<s>.json. Every side is served at the temperature fitted on its own development rows, unless the round registers atemperaturepool for its arms (reads pooled, per-readsourcesallowlist,exclude_reads; refused inside any panel a temperature-dependent criterion reads; round 20).validatealso refuses a pool that shares data with any arm's training (kev.rounds.pool_conflicts): (a) a pooled suite is an arm's training suite, a component its manifest names (sft-v1'sinputs.components) or a plan'sdatasuite; (b) it pools sources the arm trained on; (c) it reads thecalibrationordevelopmentpartition of any training corpus (a suite with trainable sources). The arm's training comes from its study in the spec or its trial'sprovenance.json(manifest hash); a checkpoint arm is covered by the round's trial arms or namestrained_on; training it cannot establish or list (no provenance, a manifest without sources, adatafile outsideevals/) is a problem for a new round, archived for a recorded one. Sources are compared by name, so pools use sources that are eval-only in Kev by construction; asourcesallowlist must name sources its suite lists. Why: round 19 fitted its SFT arms' temperature onsft-v1development rows, held-out items of the training sources, so in distribution (T 0.955, breadth-v1 ECE 0.059); round 20's pool of held-out datasets gave 0.0085 on the same checkpoint. Each arm's read-out recordstemperature_source(the pool with its reads and question count, or its trial's development rows and their suite; parents inparent_temperature_source: their trial's development rows plusshipped, head.pt's T when it is local or in the HF cache) and the printed table warns (!!!) when an arm is served at a training corpus's rows. From round 21 (POOL_REQUIRED_FROM) a rule or confirmation criterion the temperature moves (anything but accuracy) without atemperaturepool is a problem, sovalidateandlaunchrefuse the round; rounds <= 20 only get the warning, so their specs keep validating.validate/launchalso warn (report only) when a parent's served T, fitted on a training corpus's development rows, is more than 0.05 from its shipped head.pt T. A panel with"by_length": true(or{edges, tokenizer}) adds acc / ECE / Brier / confident errors per state-token bucket (kev.metrics.calibration_by_length: under_8k, 8k_16k, 16k_32k, 32k_64k, 64k_plus and the tails 8k_plus/16k_plus/32k_plus; tokens = the encoded state segment,<state>included, askev.model.encodebuilds it andkev.servereports it, counted from the reads' suite records withkev.suite.ADMISSION_TOKENIZERunless given), and a criterion may read a bucket (long.ece_16k_plus.candidate <= 0.05). Panel filters (round 24,kev.rounds.panel_filter; the same rows leave both sides):source(a name or a list),exclude_sources,exclude_tasks, andexclude_file{path, sha256}(a JSON ofids/sources/tasks, checked against its hash; a private list, e.g. tasksource-heldout families, stays out of git and is restored withscripts/private_rows.py, so the public spec carries only the path and a salted file's sha256);"optional": truemarks a report-only panel that no criterion or rank may read and whose absent reads never leave an arm incomplete; thetemperaturepool'sci: {level, samples, seed}adds each arm's bootstrap interval of its pooled T (temperature_ci). An arm may be a checkpoint without a trial ("checkpoint": "/runs/...", its "transfer" rows from atransfer_read), e.g. a WiSE-FT interpolation (scripts/interpolate_checkpoint.py,modal_app.py::interpolate: with the base, or--towardanother checkpoint of the same base and revision, full weights or a LoRA merged in fp32,--blend_headto blend the pointer heads too; round 23); a LoRA checkpoint as a full-weight one, to startkev.train --full_ft 1 --init_fromfrom it (scripts/merge_lora_checkpoint.py,modal_app.py::merge_adapter --lora <repo@rev> --out /runs/... [--like <full ckpt>]: the same fp32 W + delta rounded once as--towardat α 0, the LoRA's head and meta withlora 0,weights "full";--weights_dtype bf16writes an fp32-trained Kev as bf16, one rounding after the fp32 merge, which is how the MLX full-weight path is checked on small models; Kev-27B at/runs/r25-init/kev-27b-merged/checkpoint, weightsf61fb0c5…, ~6 min on 8 CPUs,runs/r25-init/kev-27b-merged/merge.json); deltas arekev.rounds.paired(2,000 resamples, seed 0, micro). Rounds 5-18 are recorded specs ("archive": "research-archive-2026-09-24"): their plans, reads, data builders and per-round scripts live on that git tag, not on main;validatelists what this checkout lacks instead of failing, andlaunch/watch/launch-readsrefuse a recorded round (a new round is a new spec, withoutarchive, and must have its plans and parents' reads). A read of a removed suite (kev.suite.REMOVED_SUITES: scienthoon-v1, last read by round 22) is listed as archived for rounds up to itslast_round(with or withoutarchive; their read-outs come from the committed rows), refused for any later round, and never launched again (read_commands,load_split).tests/test_rounds.pyreproduces the committed read-outs of rounds 5-18 and verdicts of 8/10/11/12/15 exactly; offline (CI) it runs round 5's read-out, round 15's locked verdict and round 20's six interpolated arms, whose rows are on main; round 19's read-out (and round 20's whole read-out, and rounds 22's and 24's) runs where the private dataset is readable (round 19's trials'sft-v1development rows; rounds 22's and 24's tasksource-heldout reads, round 24's exclusion list, and ood / agents / guardrails rows:scripts/private_rows.py); every other case skips unlessKEV_ROUNDS_ROOTpoints at a checkout with the archived rows and outputs (the research checkout, or a worktree of the tag plus its gitignored trial rows).kev.autoresearch session <specs> --spend-start X --spend-cap Yruns registered rounds to their read-outs under a spend cap and never confirms; it also keepsleaderboard,release-check,compare. An unattended session followsdocs/autoresearch.md(start, spend rule, registration, run, what may not be touched, resilience, reporting, gotchas). Model-card numbers:scripts/release_numbers.py --release <name>readsexperiments/releases/<name>.json(each arm atrial, served at its development-rows T, or a checkpoint with the round's registered temperaturepool, askev-27b-r23). - Frozen suites:
evals/<version>/<suite>/{manifest.json, *.jsonl}with partitionstrain/calibration/development/test. Manifests pin dataset + base revisions and the sha256 of every partition. Current:evals/v7/decision-v7(the release recipe),evals/v8/decision-v8,evals/v4/transfer-v4andevals/v9/transfer-v9(MMLU-Pro, buried states, unknowable) for OOD,evals/round3/{decision-r3,transfer-r3}(calibration audit; the 1,260-record final panel was read by the 2026-09-22 release confirmation and has been a short-state guard since round 11),evals/smoke-v1for tests, plusevals/external/(semif-v1, report only: sound labels but saturated, 130 of 144 right for every Kev-27B checkpoint; ekzhang-mmlupro-v1). Removed as unsound for a gate (kev.suite.REMOVED_SUITESrefuses a read with the reason; the committed rows underruns/, e.g.runs/kev-*-{wanli,typesafe}-v1, stay as the record):external/scienthoon-v1on 2026-09-27, and on 2026-09-30external/wanli-v2andwanli-v1(a quarter of the pairs have two disagreeing annotators and a gold set to one of them) andexternal/typesafe-v1(gold = the argmax of two closed frontier models' averaged answers; split-half r about 0 across checkpoints), with their builderscripts/freeze_semif_external.pyandscripts/compare_typesafe.py. There is no pooled-externals guard: round 24's audited rule, which rounds 23-26 follow, reported SemIf, WANLI-v2 and TypeSafe without gating them (PLAN.md, standing rules)),evals/night2/(delta training data,scripts/build_night2_data.py),evals/diagnostics/(binding-v1),evals/hard-v1(programmatically labelled skill records in seven families: long policy documents, trade-offs, probability, multi-hop, temporal/numeric, judging a proposed answer, missing-fact abstention;scripts/build_hard_v1.py+hard_v1_{common,policy,families,numeric}.py, labels from each family's solver over_meta.facts, templates 0-3 train / 4 development / 5 test; long_policy states reach ~5k tokens, so train with--max_state>= 5120; its 23 MB train partition is not in git;load_splitfetches it from the kev-suites mirror (since 2026-09-30) and the builder regenerates it byte for byte, ~1 min;scripts/screen_overlap.pychecks it against JevBench's public items, counts only, inoverlap.json),evals/devtools-v1(developer-tooling decisions from six licence-checked sources, human / heuristic / by-construction labels, no LLM labels;scripts/build_devtools_v1.py --reproduce-v1rebuilds it byte for byte from cached downloads; When2Call and prompt injection are eval-only. Known defects, frozen: CodeReviewer ids came from the dataset's non-uniqueidfield, socodereviewer/cls-test/13657names two development records andcodereviewer/cls-test/19245two test records (paired comparisons drop both; 66 more ids repeat inside train or across train and an eval partition, so check train/eval overlap bytext_sha256), and its CodeReviewertext_sha256hashes the hunk withoutlines_before_hunk. Without--reproduce-v1the builder makes line-based unique ids, keys the whole state and admits commitpackft records after their message question is added; see its docstring), and the real-document suitesevals/documents-v1(CFPB complaint narratives, product + issue Choice questions; its 23 MB train partition, Kev-4B's round-8 delta data, is not in git;load_splitfetches it from the kev-suites mirror (since 2026-09-30)) andevals/documents-v2(held-out test only, private mirror, manifest only):scripts/build_documents_v{1,2}.py->label_documents_v1.py(AI Gateway teachers/judges, spend ledger) ->freeze_documents_v1.py(no flag: report + adjudication queue;--combine,--spot-check,--freeze ... --min-agreement 47); label provenance inruns/documents-v1-work/.evals/breadth-v1is the eval-only generalisation panel mirroring the community Decision Index's five areas: 14 held-out datasets, MuSR, SATA-Bench, ChessBench, ContractNLI, HellaSwag (ActivityNet half), CLINC150, SGD/SGD-X, BRIGHT, BFCL, ToolRet, API-Bank, RouterBench, Humicroedit, cfcolor; GPQA skipped (gated). 150 records per dataset in development and in the locked test; >10-option tasks restricted to candidate subsets that contain the answer. Private mirror (manifest only in git; SATA-Bench NC, SGD share-alike, Humicroedit / cfcolor unlicensed text must not be public).scripts/build_breadth_v1.pyrebuilds the partitions byte for byte;scripts/breadth_report.py --suite evals/breadth-v1 --result NAME=DIR ...scores rows per area with the Index's chance correction; baselines inruns/breadth-v1-*. These datasets must never enter a training corpus.evals/longdoc-v1is a report-only long-document probe (eval-only, private mirror, development + locked test): state-length buckets 4k / 8k / 16k / 32k / 64k under the Qwen3.8-27B tokenizer, CUAD contracts (CC BY 4.0, expert clause labels, a target padded with other contracts) and generated agreement bundles (scripts/build_longdoc_v1.py+longdoc_v1_synthetic.py, byte for byte). Its manifest recordskev.suite.SERVING_CONTEXT;kev.benchmarkscores its 4k-16k buckets on the exact path and its 32k / 64k rows through the long-row rule (>ROW_PASS_TOKENS: state once, fused kernels,kernels: efficient). Read-outscripts/longdoc_report.py(--paritycompares two reads row by row), overlapscripts/screen_longdoc_v1.py, serving costscripts/longdoc_serving.py(modal_app.py::script); results inruns/longdoc-v1-report/README.md. Its synthetic part is at ceiling for every system read.evals/sft-v2is the extended SFT corpus (private mirrorjaredpalmer/kev-private-train, manifest only; built by kev-sftassemble-v2,assemble/build_v2.py): sft-v1 plus the private components tasksource-v1, longify (8k-64k states built from sft-v1 train), longdoc (code-assembled long documents), ood, tone (calm / frustrated / angry minimal pairs), injection, agents, guardrails-pii and guardrails-grounding, each hash-checked against its own manifest (recorded with path, commit and mirror revision underinputs.private_components). tasksource-v1's family list stays private: its records carry sourcetasksource. Every record is admitted at 64k states and screened (kev-sftassemble/screen_v2.py, the kev-sft screen rule) against every evaluation partition of every frozen Kev suite, the private evaluation mirror, JevBench public and the eval-only suites below; a non-clean_meta.variantmoves to_meta.twin(kev.benchmarkscores only clean rows). Its calibration/development partitions are held-out items of the training components: in distribution, in-trial screening only.evals/sft-v2-r21is the same records with states of at most 32,768 tokens, round 21's training suite (PLAN "Round 21"; one epoch of the 64k corpus does not fit three 8-hour attempts on 8 H200s, so it also caps each of sft-v1's public sources at 1,200 train records and keeps only states up to 8k tokens in calibration/development, the in-distribution screening partitions no rule reads; none pairs gated at 8k,--none_pair_max_state). Eval-only suites set aside by its components (private mirrorjaredpalmer/kev-private-evals, development only, report-only; source names shared with sft-v2's training records, so never a temperature pool):evals/ood-v2,evals/agents-ood-v1,evals/guardrails-ood-v1(PII + grounding + injection).evals/tasksource-heldout-v1(development + locked test; 24 whole dataset families held out of tasksource-v1: held-out datasets) publishes hashes and counts only; its family list is in the private kev-sft manifest, so its rows and per-source reports stay in the private dataset (scripts/private_rows.py), and with no sources listed it cannot be a temperature pool read.kev.jev --count-refusalscounts HTTP 400/413/422, and a 5xx on a request past 1.5 x Jev's ~32k-token context (the gateway answers oversize with either), as refused. Partitions over ~10 MB are not in git; they are mirrored at the Hub datasetjaredpalmer/kev-suites(revision pinned inkev/suite.py: SUITES_REVISION) andload_splitfetches + verifies them on first use. After freezing a new suite:hf upload jaredpalmer/kev-suites evals . --type dataset --include "*.jsonl" --include "*.json", bumpSUITES_REVISION, gitignore the large partitions. Never modify a frozen file; new data = new version. Held-out suites whose text must never be public (a private test set) keep onlymanifest.jsonin git and name their own mirror in it,"mirror": {"dataset": "jaredpalmer/kev-private-evals", "revision": "<commit sha>"}(kev.suite.PRIVATE_DATASET, a private dataset repo): freeze locally,hf upload jaredpalmer/kev-private-evals evals . --type dataset --include "<suite path under evals/>/*.jsonl"(e.g.held/documents-v2/*.jsonl), write the returned commit into the manifest, gitignore the partitions.load_splitfetches and hash-checks them for accounts with access (hf auth login/HF_TOKEN; Modal images already carry a locally fetched copy underevals/) and raises a PermissionError naming the repo for everyone else;tests/test_conventions.pyfails if such a suite tracks a partition. - Modal (default for anything beyond smoke):
modal_app.py;uv run modal run modal_app.py::{smoke,study,pull,resume, locked_test,evaluate,base_probe,benchmarks,smoke_base,anchors,sft_probe,gpu_tests,interpolate,release_copy,release_publish,release_verify};uv run modal deploy modal_app.pyonce so studies survive a disconnect. Image =uv_syncof pyproject/uv.lock (Linux torch wheel is CUDA) + fla, triton>=3.7.1 and the causal-conv1d wheel (--no-deps, or it reinstalls torch's triton 3.4) +kev/+evals/+tests/; Volumeskev-hf-cache(HF_HOME) andkev-runs(trial outputs, pulled toruns/<study>then ranked bykev.experiment --aggregate).KEV_GPUpicks the GPU type (H100 default; T4 for the free tier),KEV_APP_NAMEisolates a research deployment,worker_environmentpropagates app/GPU/secret settings (a dependency list that differs inside the container fails with "Function has N dependencies but container got M"). Legacy checkpoints: Hub id, ormodal volume put kev-runs runs/<run> /legacy/<run>then--existing /runs/legacy/<run>. Eval on CUDA turns TF32 and fused SDPA off inLocalPredictor(what "fp32-exact" means and does not, below); training keeps TF32 and may use--dtype bf16. Probes, external benches and new-base fit checks lived inmodal_probe35.pyuntil 2026-09-21 (folded in at 90990a5); the how-to is thekev-modal-studyskill. - Figures:
uv run python scripts/plot_family.pyanduv run python scripts/plot_tweet.pyregenerate docs/kev-family.png and docs/kev-benchmark{,-dark}.png from saved result files. Style lives inscripts/chartstyle.py(Geist type, Vercel color tokens, direct labels, no legends, one label/plot/value lane per bar set); new figures should import it rather than set their own rcParams.kev.plot(loss curves from train logs) is a debugging aid, not a README figure. - Current family (2026-09-21, all Qwen3.5 + the dates/unknowable delta):
jaredpalmer/kev-9b(v2 since 2026-09-30: round 27'sr18-9b/00-trial-0, the night2 checkpoint + one epoch on round 15's joint documents-v1 + hard-v1 + devtools-v1 data with replay 10000 (round 18, arm (a)), staged at/runs/release/kev-9b-r27/checkpoint, adapter2b2a70cf…, T 2.19 from round 24's held-out pool; v1,night2-9b-du/00-trial-0at T 2.30, at tagv1),jaredpalmer/kev-4b(r10-skills/00-trial-0from the round-10 release: the round-8 checkpointr8-small/00-trial-0(the night2 checkpoint + one epoch ondocuments-v1train) + one epoch onhard-v1+devtools-v1train; round-8 weights at tagr8-documents-release, night2 atnight2-du-release; Qwen3 weights at tagqwen3),jaredpalmer/kev-0.8b(r15-08b/00-trial-0from the round-15 release:night2-08b-du2/00-trial-0+ one epoch ondocuments-v1+hard-v1+devtools-v1train together, replay 6000; previous at tagnight2-du-release),jaredpalmer/kev-27b(v2 since 2026-09-30: round 23's27b-k-w85, full bf16 weightsd27af6ab…(0.85 round-22 full-weight SFT + 0.15 v1), staged at/runs/release/kev-27b-r23/checkpoint, T 1.32 from a held-out pool; v1, the LoRAr6-27b-v2/01-trial-1from "B1 v2" inPLAN_27b.mdat tagresearch-archive-2026-09-24, at tagv1-lora; baseQwen/Qwen3.8-27Brev1d4bf0f2, post-trained, not-Base; bf16 only (51 GB of weights, ~66 GB resident when serving), so B200, H200 or H100 80 GB, no Mac path; the kev-deploy skill defaults it to B200, then H200, then H100; served fused with CUDA graphs like the others (v2:runs/serving-27b-r23; v1:LoadOptions.fusedfolds the adapter into the bf16 backbone,runs/fused-27b-*)). Pre-delta v7 checkpoints at tagv7-base(q35-9b/01-trial-1,q35-4b-s23/00-trial-0,q35-08b/02-trial-2). Calibration is built into each checkpoint:head.pt["temperature"](fitted byscripts/calibrate_checkpoint.pyon the trial's development rows; 27B 1.32 (on a held-out pool, round 23; v1 1.38), 9B 2.19 (on a held-out pool, round 27; v1 2.30), 4B 2.41 (2.96 after the round-8 delta, 2.14 before it), 0.8B 2.35 (2.41 before the round-15 delta)) is applied byPointerHeadin eval mode;KEV_TEMPERATURE=1.0overrides to raw. Those were fitted in distribution; the script now refuses rows that share data with the checkpoint's training (its own suite, sources it trained on, a training corpus's calibration/development partition;kev.rounds.pool_conflicts, training from head.pt or the trial's provenance, rows' suite from the kev.benchmarkreport.jsonbeside them) unless--allow-in-distribution, which warns and is recorded inhead.pt["temperature_fit"]["in_distribution"]; a manual--temperatureneeds--reason(recorded);temperature_fit["fit_rows"]records each rows file's suite, partition, sources and question count. Ship a temperature fitted on held-out datasets (round 20's pool). A trial's owncalibration_fit(result.json,calibration/temperature.json) is labelledrole: "in-trial screening; ... not a served or shipped temperature". The script also reports an out-of-fold grouped-CV ECE with bootstrap CIs alongside the in-sample fit (4B: 0.075 raw -> 0.020 OOF, separated) and stores it underhead.pt["temperature_fit"]["cross_validation"]; re-run it after any new checkpoint before publishing. Opt-in:KEV_DATE_FACTS=1(day counts). Delta data: evals/night2/ (scripts/build_night2_data.py). Previous generation, kept for Mac latency:kev-8b,kev-0.6b,kev-4b@qwen3(cards*-qwen3.md). Qwen3.5 backbones are hybrid (Gated DeltaNet):DecisionModel.hybridroutesforward()(sokev.benchmark) throughforward_rows_batch(one causal row per question, state repeated) and serving andprobs()through_branch_rows_from_prefix(state once, #77); the packed block-causal mask is only valid on attention-only bases. Needs transformers>=5.17, peft>=0.21; CUDA wantsflash-linear-attention+triton>=3.7.1(in the Modal image). MPS has no fast DeltaNet kernels, so on Apple Siliconkev.serveruns these checkpoints throughkev/mlx_model.py(mlx-lm's Metal kernels; M5, 5 questions on a ~270-token state: Kev-4B 721 ms new state / 136 ms cached state vs 3302 / 847 ms for torch bf16; parity with fp32 torch on the full decision-v7 development partition: 4B max |dp| 0.025, 1 flip in 1,264 questions; 0.8B max 0.054, 4 flips (runs/r4-mlx-parity-*)). Long states on the MLX server (scripts/mlx_long_states.py, M5 32 GB, 3 questions, one about a fact planted at 60% depth,runs/mlx-long-states/summary.md): the state runs into the prompt cachePREFILL_CHUNK= 1,024 tokens per pass (one pass kept every token's activations: Kev-4B at 8k took 4.6 GB above the weights instead of 1.2, same 6.5 s; exact up to float reassociation: fp32 on the CPU matches one pass to 1e-6, the 4B's first 8 layers included, while bf16 on Metal moves answers by up to ~0.01 (4B: 0.02) at any chunk size from 256 to 4,096 (a few tokens' keys/values in the 4B's deep layers move up to 0.7 of their norm, the same tokens wherever the passes start), since the kernels are picked by shape (a state of exactly k x 2,048 tokens happened to keep the one-pass bits at 2,048; real lengths do not), so the smallest chunk that costs no time wins;--prefill-ab,runs/mlx-long-states/prefill-ab-*.json; a state of up to 1,024 tokens is still one pass), andPrefixCache.make_roomdrops the states a batch will evict before it runs. Kev-0.8B at 8k / 16k / 32k / 65k: 1.4 / 3.2 / 8.0 / 21.2 s for a new state, 74 / 94 / 134 / 202 ms cached, 2.7 / 3.0 / 3.1 / 3.8 GB MLX peak (1.5 GB weights), planted fact right at every length, max |dp| 0.008 / 0.005 vs fp32 torch on the CPU at 8k / 16k. Kev-4B: 6.6 / 14.0 / 30.5 / 84.5 s new, 354 / 427 / 533 / 716 ms cached, 10.2 / 11.0 / 11.9 / 13.0 GB MLX peak (8.4 GB weights; 14.1 GB process footprint at 65k), planted fact right at every length (p 0.94-0.97, moves with the note when it names another office); its fp32 torch reference (18.6 GB) did not fit. Kev-9B was not run: its 19.3 GB of weights plus a working set did not fit the 19.8 GB that machine had free (other apps, 25 GB of swap in use); it has the 4B's attention layout (8 attention layers, 4 KV heads of 256: 33 KB of keys and values per state token), so a 65k state should add about what it adds to the 4B, ~4.5 GB. Plan and results: PLAN.md at tagresearch-archive-2026-09-24, History > "Qwen3.5 port". Full-weight checkpoints (Kev-27B v2) run on MLX too:kev.mlx_model.load_fullbuilds mlx-lm's qwen3_5 model from the checkpoint's ownconfig.json(model_typeqwen3_5_text) and shards,language_model.model.prefixed onto the saved names, mlx-lm's sanitize for a transformers export (conv kernel axes, norms as 1 + w), LM head deleted before anything is materialized; strict (every name, shape and the head.pt dtype must match, else ValueError), nothing merged or cast. Load peak = the weights: Kev-4B written as bf16 full weights, 8.41 GB MLX peak for 8.41 GB of shards vs 15.87 GB for the LoRA path's merge; the same answers as the LoRA path bit for bit once the base's fp32 A_log / gated-norm tensors are rounded to bf16 as a full export holds them (otherwise max |dp| 0.018); vs fp32 torch on the same checkpoint max |dp| 0.015, 0 flips in 60 (runs/mlx-full-{4b,0.8b},scripts/mlx_full_parity.py). Kev-27B v2: 51.2 GB of weights, so expected ≈ 51 GB plus working memory at load and after; untested on a Mac (PR #175 served v1 through the LoRA path on a 128 GB M5 Max: 52 GB steady, 97 GB peak at the merge). - Delta fine-tuning:
kev.train --init_from <run dir | Hub id[@rev]>warm-starts LoRA + head (compatibility checked before load; source hashes in provenance; allowlisted inkev/experiment.pyso studies can run cheap delta trials from a released checkpoint). Use lr <= 2e-5 for deltas. A full-weight checkpoint warm-starts a--full_ft 1run (every backbone tensor copied over the base, coverage checked); LoRA and full do not mix. - Checkpoint layouts (
kev.checkpoint, the loader rule):adapter_config.json-> LoRA onhead.pt's base; no adapter andconfig.json+model*.safetensors(save_pretrainedof the bf16 backbone, 5 GB shards + index) -> full weights, loaded from the checkpoint directory (meta.weights == "full",lora == 0,base/base_revisionkept for the tokenizer). Full weights load in bf16 by default (KEV_DTYPE=fp32upcasts); fused kernels and CUDA graphs apply as to a merged adapter; on Apple Siliconbackend=autoruns them on MLX as saved (kev.mlx_model.load_full). - Publish:
uv run python -m kev.publish --run runs/<run> --repo jaredpalmer/kev-<size> --card docs/model-cards/<name>.md(needshf auth login;--private,--tag,--revision <branch>for candidates;--privatecreates a missing repo private and refuses a public one,kev.mirror.ensure_private). A full-weight checkpoint on the volume is staged and uploaded from CPU containers, never through the laptop:modal_app.py::release_copy --src /runs/.../checkpoint --dst /runs/release/<name>/checkpoint --expect <weights sha256>(scripts/release_checkpoint.py: refuses an existing destination, checks the copy's weights hash), then the release temperature into the copy's head.pt (scripts/calibrate_checkpoint.py), thenmodal_app.py::release_publish --run /runs/release/<name>/checkpoint --repo <private repo> --card <card>(kev.publish --private; shards linked, not copied). The approved public release: tag the current main first (kev-27b@v1-lora), thenrelease_publish ... --public --confirm-public <repo> --replace(kev.publish --replacedeletes every file the upload does not carry in the same commit; a full-weight upload into a repo still holding an adapter is refused without it, since the loader rule would pick the adapter), thenmodal_app.py::release_verify --jobs "<repo>@<suite>,..." --out runs/<name>(an H200 with no secret, no HF cache volume and no token) andscripts/compare_release_rows.pyagainst the round's committed rows. Kev-27B v2 (PLAN.md "Released: Kev-27B v2", recordruns/release/kev-27b-r23-published.json) is the worked example. Repos are named by base model size (Kev-0.5B = Qwen2.5-0.5B); versions within a size are Hub tags (hf repos tag create jaredpalmer/kev-0.5b vX.Y). Collection: huggingface.co/collections/jaredpalmer/kev-6aad9d0ea49f2589665e07cd.--runin serve/benchmark accepts a Hub id. - HF Space (public demo, ZeroGPU): huggingface.co/spaces/jaredpalmer/kev. Source in
space/(Gradio 6app.py,presets.pymirrors the playground presets,README.mdfrontmattermodels:/datasets:is what links the Space from the model and dataset pages). Publish withscripts/publish_space.sh [repo] [message]: it stagesspace/+kev/{__init__,model,api,checkpoint}.pyinto onehf upload --type spacecommit, so the vendored modules never drift from the repo (a stale vendoredmodel.pyis how the hugging-apps Space broke on Qwen3.5). Any change tokev/model.py,kev/api.pyorkev/checkpoint.pythat affects serving should be republished. ZeroGPU rules:import spacesfirst, load on CPU in fp32 (PeftModel.from_pretrained(..., torch_device="cpu"), otherwise peft picks the faked cuda device and crashes), merge, then.to("cuda")once at module scope; a restart reloads both models (~3 min). Check withhf spaces logs jaredpalmer/kevand the gradio_client/decideendpoint; the Space is also in the Kev collection and needs PRO to exist. - Serve:
uv run --extra serve python -m kev.serve --run jaredpalmer/kev-4b --port 8009(--rundefaults to runs/kev and falls back to runs/smoke; the playground proxies :8009)- TypeSafe-compatible:
POST /v1/systemone,GET /v1/models(model cards forkev-latestandjev-latest, plus device, dtype, temperature and prefix-cache stats), anx-typesafe-request-idheader on every response, and bearer auth whenKEV_API_KEYis set (unset = open server). - SDK:
TypeSafeClient(api_key="local", base_url="http://127.0.0.1:8009", model="kev-latest") - Admission (
kev.model.admit, the one check kev.serve and the Space call, the same on torch and MLX): a state overSERVE_MAX_STATEtokens (65,536,<state>included) gets a 422 naming its count, the limit and the fixes (the TypeSafe SDK raisesTypeSafeUnprocessableEntityErrorwith it, not retried), like a question row overSERVE_MAX_BRANCH; until this was fixed the server cut the state silently (onlyusage.input_tokensshowed it; every Kev-27B deployment at kev-deploy's f2bb629 pin read 8,192 tokens).KEV_TRUNCATE_STATES=1(Server.truncate_states) reads the first 65,536 tokens instead, and every response of such a server carriestruncatedplususage.state_tokens/state_tokens_used(the SDK ignores the extra fields); a default server's body is unchanged./v1/modelsreportsmax_state_tokensandtruncate_states. No per-request switch: TypeSafe's own endpoint would ignore it. Benchmarks never truncate (LocalPredictorencodes strictly within the suite's context);--remoteturns an endpoint's 400/413/422 intoContextOverflowat once (rejected underskip_overlong, else a clean stop), retries only 408/429/5xx and connection errors. - CUDA: bf16, fused kernels and CUDA graphs by default (
LoadOptions.fused/LoadOptions.cuda_graphs,KEV_FUSED=0/KEV_CUDA_GRAPHS=0to decline; fused only when fla 0.5.2 is installed,checkpoint.fused_available, not in the serve extra). A server pass was kernel-launch bound (~60 ms on an H100 at any length).kev/fused_qwen35.pyrewrites the merged Qwen3.5 layers with fla Triton kernels (it needs fla 0.5.2 exactly, pinned in the images, and refuses others: it patches fla's NB-keyed kernel launches; a pass continuing a cached DeltaNet state does not write it back).kev/cuda_graphs.pyreplays bucketed passes (state left-padded, rows right-padded, masked exactly; equal to eager up to bf16 reassociation) and owns admission (admits), batches (run), capture policy (capture_due: idle, or a bucket that keeps recurring) andstats(); a failed capture leaves its bucket eager.kev.serve.Serverruns every pass on one model thread that batches whatever is queued (DecisionModel.probs_batch,CudaGraphs.runover typedRequests;probs_oneis the per-request rule every backend shares;PrefixCachekeeps only the states that survive a batch;length_groupsgroups passes by a token-cost model,PASS_TOKENS; long new states take their eager state passEAGER_STATESat a time);/v1/systemoneis async,Server.lockexcludes the model thread,wait_idle()waits for answers and captures, every response carriesserver-timing. Measure withuv run modal run modal_app.py::serving --run <hub id> --gpu <GPU> --name <name>(scripts/serving_bench.py: latency, parity vs fp32, throughput at 1/8/32/64 in-process clients,--flags=--isolationfor question isolation on the served path; reports inruns/serving-*(graphs only),runs/fused-*and, for the current checkpoints and code,runs/serve-*/runs/grouping-4b-h100/runs/fused-27b-*, which the README serving table reads).tests/test_model.py::test_cuda_graphs_match_eagerneeds CUDA (run it on Modal). Over HTTP, Modal'sasgi_apppath caps a container at ~40-50 req/s in earlier measurements; the current direct Modal Server and rejected higher-limit web endpoint are paired byscripts/modal_http_bench.py(runs/modal-http-4b-h100/report.json: HTTP/process/model times, errors, kernel set). Loading merges the fp32 adapter straight into bf16 weights (same bits as the old fp32 merge + cast), so Kev-9B needs ~17 GB, not 36 GB.
- TypeSafe-compatible:
- Extra endpoints for the demo:
POST /v1/systemone/permute(one Choice under n option orders),POST /v1/systemone/separate(each question alone; packed-vs-separate comparison)./v1/systemonealso returnslatency_ms. - Web demo:
cd playground && npm run dev -- -p 3001(:3000 is used by another project). Next 16 app router;/kev/*is rewritten to the FastAPI server (KEV_API, default http://127.0.0.1:8009); it uses only the/v1/*routes. Presets live inplayground/src/lib/kev.ts./chess(src/components/chess-game.tsx,src/lib/chess.ts, chess.js): legal moves -> Choice options, board -> state, Score for eval; games in localStorage keykev.chess.v1.- React Compiler lint forbids sync setState in effects; schedule via setTimeout or move into handlers.
- Next 16 dev only trusts
localhost; other hostnames needallowedDevOriginsor the page SSRs but never hydrates (no console errors).127.0.0.1is allowed innext.config.ts. Verify hydration withagent-browser(CDP), not curl. playground/AGENTS.md(regenerated bynext dev) is the Next 16 rules file; readnode_modules/next/dist/docs/before writing app code.
- Unit tests (no weights; the CI
pythonjob):uv run --extra serve python -m pytest tests/test_unit.py tests/test_research.py tests/test_generators.py tests/test_conventions.py tests/test_documents_tools.py tests/test_hard_v1.py tests/test_devtools_v1.py tests/test_breadth_v1.py tests/test_rounds.py tests/test_skill_scripts.py -q.tests/test_skill_scripts.pycovers the kev-finetune stdlib scripts (stdlib only, no network). Weight-backed parity tests (runs/smoke-hl/00-trial-0/checkpoint+ a Qwen2.5-0.5B / Qwen3.5-0.8B-Base download, ~2.5 min, local only):tests/test_model.py;tests/test_mlx.py(Apple Silicon, ~30 s) for the MLX backend.test_conventions.pyis a table of "one canonical home" rules (head.pt viakev.checkpoint,KEV_*viaLoadOptions.from_env, option keys viaapi.question_keys, context viamodel.fits/MAX_PACKED, device viakev.device, ...); add a row when a new helper becomes canonical. It also asserts every published README/model-card number indocs/claims.jsontraces to a committed report (scripts/verify_claims.py). - API tests (server must be up):
KEV_BASE_URL=http://127.0.0.1:8009 uv run --extra serve python -m pytest tests/test_api.py -q - CI (.github/workflows/ci.yml) runs the unit job above and a
playgroundjob:npm ci && npm run lint && npx next typegen && npx tsc --noEmit -p ..
Repo skills (.agents/skills, tracked in git):
kev-verify: how to prove a change has no regression (unit suites, weight-backed parity, worktree parity harness against main) and ship it as a stacked, reviewed, squash-merged PR.kev-pr-description: how to write the PR title and body (the acdlite / sebmarkbage essay style, with a weak/strong pair from a real Kev PR). Read it before opening any PR.kev-modal-study: launching, monitoring and pulling Modal studies, base probes, remote benchmarks and new-base smoke checks.thermonuclear-code-review: the strict structural review (standards adapted from cursor-team-kit's thermo-nuclear review, the approval bar, and the table of canonical homes for shared rules).kev-knowledge: how to search the knowledge graph of every earlier Devin session on this repo (the private repojaredpalmer/kev-knowledge: topic notes, session notes, timeline, PR back-links; refreshed nightly by a Devin Cloud automation from the Devin API) before re-deriving history. Read it when asked what was tried, decided or measured before, or why a rule exists.
Installed from other repos by npx skills add and pinned in skills-lock.json (deslop, unslop from cursor/plugins,
grill-me from mattpocock/skills); .agents/skills/modal/ is gitignored and reinstalled with uv run modal skills install.
Published from this repo: skills/kev-deploy (npx skills add jaredpalmer/kev@kev-deploy), one self-contained Modal file (scripts/kev_serve.py, pinned KEV_REF, GPU list per released model from runs/serve-* / runs/fused-*: 0.8B L4, 4B L40S, 9B H100, 27B B200 (H200, H100); CUDA graphs captured at start (server.wait_idle()); 64 concurrent web inputs per container, autoscaling at 32; optional KEV_REGION; KEV_FLASH=1 = direct HTTP via app.server, target 64 per container, needs KEV_REGION, keeps one paid container up, tested with Modal 1.5.5; fla pinned to 0.5.2; bearer key via a deploy-time Modal secret) that serves a released Kev checkpoint as a System One endpoint in one modal deploy; bump its KEV_REF after a serving change it should pick up. And skills/kev-finetune (npx skills add jaredpalmer/kev@kev-finetune): the user-facing fine-tuning skill (SKILL.md for agents,
README.md is the human cookbook, references/ the long-form docs). agentskills.io format (validate with uvx --from skills-ref agentskills validate skills/kev-finetune).
Stdlib scripts: extract_workload (find Jev/TypeSafe call sites + labelled files, draft the spec), convert_data (CSV/JSONL -> records),
generate_data (OpenAI-compatible endpoint), plan_size (paired McNemar sizing; --from-result post hoc), split_data (--holdout keeps real rows out of train).
scripts/kev_modal.py is a self-contained Modal app (app kev-finetune, volumes kev-finetune-runs + kev-hf-cache) whose image clones this repo at KEV_REF
and pip-installs it, so it needs no local clone; bump KEV_REF after merging a kev/ change the skill depends on. Launch-time settings go into the image env via
SETTINGS (a Secret list that differs inside the container fails with "Function has N dependencies but container got M"). train derives every architecture flag
from the init checkpoint's head.pt; results are the skill's own result.json shape (not a research trial's); publish is private by default; teardown removes
runs / the endpoint / the volumes. Tests: tests/test_skill_scripts.py.
kev/data.pydataset -> typed records (Source,build,materialize,load_records), trainable/eval-only source policy, permutation / none-of-the-above / distractor augmentationkev/composition.pygenerated rule structures (atoms, shapes, groups) behind the compositional training and eval recordskev/contrastive.pyprogrammatic contrastive pairs with code labels: one sentence changed flips the labelkev/suite.pyfrozen suites: digest/manifest/read_manifest/load_split (Hub mirror), CONTEXT + admission,validate_training(trainable/eval-only policy),semantic_hash, UTF-8 JSON helpers, freeze CLIkev/study_v3.pybuilds the v3+ decision/transfer suites (public pool + generated families, grouped splits)kev/transfer_v9.pytransfer-v9: transfer-v4 byte-for-byte plus MMLU-Pro, buried states and unknowable itemskev/model.pyencode(), branch_mask(),fits/MAX_STATE/MAX_BRANCH/MAX_PACKED, PointerHead (carries the temperature), DecisionModel (packed mask, orhybridrow batches)kev/full_ft.pyfull-weight training:MasterAdamW(fp32 masters, host or device), FSDP2shard,rank_share,save_backbone,init_distributedkev/train.pyLoRA (or--full_ft) fine-tune:training_requests(suite / built / --data+--replay, context filter, policy checks, mix ablations),encode_batch+batch_loss(CE, anchor KL, permutation KL, RPS, smoothing/Brier/focal),mainorchestration. Grad accumulation over small padded batches.kev/anchors.pyfrozen-base zero-shot distributions per training question, the target for--anchor_wkev/checkpoint.pyCheckpoint (resolve run dir or Hub id,head.ptschema =Meta, the LoRA-vs-full loader rulefull/shards/weights_sha256(a full checkpoint loads in its head.ptweights_dtype, which must match config.json'sdtype), load withLoadOptions-> torchDecisionModelor, withbackend="mlx"/"auto",MLXDecisionModel;warm_startfor deltas)kev/mlx_model.pyApple Silicon backend: mlx-lm Qwen3.5 backbone, LoRA merged in fp32 on the CPU stream (merge_lora) or a full-weight checkpoint loaded as saved (load_full), Kev's encoder/rows and the torch PointerHead unchanged; state prefix = mlx-lm prompt cache filledPREFILL_CHUNKtokens per pass, branches on a replicated copykev/device.pydefault device and explicit selection (select, a child-process probe: CUDA/ROCm, then MPS, then CPU;KEV_DEVICE_PROBE_TIMEOUT, default 30s) / sync / empty_cache / allocated_bytes for cuda, mps, cpukev/metrics.pypure-numpy scoring of benchmark rows: ECE, Brier, NLL, selective prediction (tie-aware coverage@error, AURC), temperature fit, out-of-fold CV calibration report (cross_validated_temperature), paired bootstrapkev/predictors.pyLocalPredictor (checkpoint), RemotePredictor (System One endpoint), JevPredictor (AI SDK worker)kev/benchmark.pyrows from predictions, summarize(), evaluate_records(), CLIkev/compare.pytwo saved result dirs -> paired bootstrap comparison + NLL sensitivitykev/jev.pyJev through Vercel AI Gateway (key provisioning, budget cap) scored on a frozen suitekev/experiment.pyconfig-only study runner: trial allowlist, gates, temperature fit, ledger, aggregate/resumekev/rounds.pyregistered rounds as specs: validate, launch, watch (pull + reads), read-out, confirmation; the served-vs-served paired readkev/autoresearch.pyspend-capped sessions over round specs (kev.rounds), plus the study leaderboardkev/evaluate.pylegacy prototype eval: acc/ECE, permutation stability, IIA shift, isolation probe, packed-vs-separatekev/plot.pyloss curve(s) from train logs + accuracy-vs-baselines bars from eval.jsonkev/api.pyTypeSafe request/response models; Noul/Choice/Score -> pointer options;question_keys; confidence formulas;with_date_factskev/serve.pyFastAPI: /v1/systemone (+ permute, separate) and /v1/models;Serverholds the checkpoint and the prefix cachekev/publish.pyrun -> Hub repo: adapter, head.pt, tokenizer, trial result/provenance/log, the card as README.md;--tag,--revision,--privatemodal_app.pyevery GPU entrypoint: trials, locked tests, probes, benches, anchors, smokescripts/one-off builders and read-outs:calibrate_checkpoint.py,build_night2_data.py,build_binding_diagnostic.py,freeze_{semif,calibration_audit}.py,{build,label,freeze}_documents_v*.py,calibration_audit.py,review_calibration_screen.py,compare_{q35,night2}.py,temperature_groups.py,base_mmlu_probe.py,plot_*.py+chartstyle.py,publish_space.shtests/test_api.pyconformance against the docs' example requests + official SDK
- All JSON/JSONL is UTF-8 with LF endings regardless of platform locale: read/write through
kev.suite.read_json/read_jsonl/write_json/write_jsonl(or passencoding=), and.gitattributespins*.json/*.jsonlto LF so sha256-checked partitions survive a Windows checkout (issue #12). runs/is gitignored except ledgers, reports, provenance and training logs (see .gitignore); never commit weights, prediction dumps or regeneratedruns/leaderboard.*.- Delimiters reuse existing Qwen special tokens (
<|fim_prefix|>etc.) to avoid resizing embeddings; pefttrainable_token_indicesleaked memory on MPS. output_hidden_states=Trueon MPS blows memory; use the bare.modelbackbone'slast_hidden_state.PolyAI/banking77uses a loading script (unsupported); uselegacy-datasets/banking77.- User text is tokenized via
model.user_tokens(), which rewrites<|name|>-><¦name¦>so callers cannot forge option/branch delimiter tokens (the fast tokenizer ignoressplit_special_tokens). - Training data is built as TypeSafe-shaped requests and goes through
api.to_record()(data.materialize), so train and serve text are identical. - Serving path (
kev.checkpoint.Checkpoint.load+kev.serve;LoadOptions.from_env()reads theKEV_*variables at CLI entry points only): backendauto(MLX for hybrid checkpoints on MPS when mlx-lm is installed and fp32 was not asked for;KEV_BACKEND=torch|mlxto force; library callers get torch unless they ask;SCORING_INTERFACEin kev/model.py names what both implementations expose), LoRA merged in fp32 then cast (KEV_MERGE=0to keep unmerged, torch only), bf16 by default on CUDA/MPS (KEV_DTYPE=fp32for the exact path; L4 numbers in README "Serving Performance"),KEV_ATTN=sdpadefault on MPS,KEV_SHAPE_BUCKET=64on MPS, state-prefix LRU (KEV_PREFIX_CACHE=4states andKEV_PREFIX_MAX_TOKENS=65536state tokens in all, a longer state is not cached; the states a batch will evict are dropped before its passes,PrefixCache.make_room;KEV_PREFIX_MIN_TOKENSdefaults to the model'sprefix_min_tokens: 384 for attention-only torch models, 0 for hybrid and MLX ones because their miss path would recompute the state per question). Checkpoints trained with--weights_dtype bf16always load bf16 with the adapter unmerged. Any change here must keep the parity tests in tests/test_model.py (merged vs unmerged, prefix vs full pass, bucket padding) and tests/test_mlx.py (MLX vs fp32 torch, prefix form vs row form, isolation; Apple Silicon only) passing; report numbers with the fp32 unmerged path.scripts/mlx_parity.py --run <ckpt>is the fuller read (60 records, latency of every path);scripts/mlx_full_parity.py --full <ckpt>the full-weight one (MLX full vs its LoRA source vs fp32 torch, each load's memory in its own process); MLX's fp32 GPU matmul is a reduced-precision fast path (~1e-3 relative on an M5), which is why the LoRA merge runs onmx.cpu. - Serving context is 65,536 tokens for the state and 73,728 for the state plus one question branch, so a question gets at least 8,192 (
kev.model.SERVE_MAX_*; 8,192 / 8,192 until the long-context PR,kev.suite.SERVING_CONTEXT_8K, which the suites frozen before it record and their builders keep);kev.train --max_stategoes up to the same 65,536 (MAX_TRAIN_STATE). The released checkpoints trained on 384 / 1,024, so longer inputs are untested for accuracy. No limit on questions per request: the row form runsrows_per_passrows per forward pass (ROW_PASS_TOKENS, a 16,384-token budget counting the cached state per row), and an attention-only model switches from the packed mask to rows above it (DecisionModel.rows_form). Evaluation is fp32-exact (as the next item defines it) except long rows: on CUDA with the torch backend, a record whose longest row exceedsROW_PASS_TOKENSruns under SDPA's flash / memory-efficient kernels (the exact math kernel's L x L scores do not fit;kev.predictors.long_row_kernels) and is labelled:"kernels": "efficient"on its prediction and rows,long_rows: {count, records, kernels, threshold}in report.json (both absent when no row is long, so other reads are unchanged;LocalPredictor). An fp32 backbone (the small family) stays fp32 there. transformers asks SDPA for grouped-query attention (enable_gqa) on an unmasked call, which is every long state, and only flash (no fp32) and math take it. So until the long-state-memory PR the fp32 state pass fell back to math, and Kev-0.8B / 4B / 9B ran out of memory on 32k-64k states (the 2026-09-30 family longdoc reads). Insidelong_row_kernelsan fp32 call repeats its keys and values instead and runs the memory-efficient kernel in fp32; bf16 calls (Kev-27B) are unchanged. On an H100, 16k / 32k / 64k states: Kev-4B 4.3 / 8.6 / 17.2 GiB over the weights and 3.0 / 6.8 / 17.2 s, Kev-9B 4.6 / 9.1 / 18.2 GiB and 5.1 / 10.9 / 25.5 s (math: 33.6-33.7 GiB at 16k, then out of memory, on an H200 too). Against math at 8k-16k: max |dp| 6e-4, 0 flips in 238 questions (Kev-4B and 9B, H100) (scripts/long_state_memory.py,runs/long-state-*). Kev-4B on longdoc-v1 development (H100,runs/longmem-kev-4b-longdoc-dev-r2) now reads all 1,200 records (its 480 32k / 64k records long, 6.3 / 16.8 s median), and its 720 records under the threshold have the logits of main bit for bit. A long row short enough to have fit before (just overROW_PASS_TOKENS) moves by up to the parity above, so pair such rows only with reads from the same side of this change. This is CUDA-only: on CPU / MPS attention stays eager and exact and still materialises L x L. A long record on a hybrid torch backbone runs its state once through the shared prefix; the MLX backend'sforwardalready does. Kev-4B on MLX, 64 questions on a 4.8k-token state: 4.3 s / 9.4 GB peak instead of 18.5 s / 24.7 GB in one pass.n_permon/permuteis 1..64. - What "fp32-exact" guarantees (
runs/drift-v1/REPORT.md). A read is bit-reproducible for a fixed kernel set: the same kev code, image (torch, transformers, flash-linear-attention, Triton, causal-conv1d) and GPU type give bit-identical logits run to run and container to container, and a kev change that leaves the scoring path alone keeps them (the 2026-09-23 code andmainagree bit for bit on Kev-27B v1). A kernel change moves them.LocalPredictor's switches reach only PyTorch. On CUDA, flash-linear-attention's Triton kernels run the Gated DeltaNet rule of every Qwen3.5 / Qwen3.8 backbone with TF32 dot products, so an fp32 Qwen3.5 read sits ≈ 0.003 in p from true fp32 (Kev-4B, semif-v1: 1 flip in 252;TRITON_F32_DEFAULT=ieeecloses it to 4e-6). A bf16 backbone (Kev-27B) is bf16 in every kernel. In both cases an upstream bit change is amplified. #125 put causal-conv1d's CUDA kernel in the Modal image, replacing transformers' PyTorch convolution. That alone moved Kev-27B v1 by max |Δp| 0.03-0.06 (0-0.4 % flips, ECE +0.0008 on breadth-v1) and Kev-4B by 6e-4, so reads from before and after #125 differ by that much. CPU / MPS reads run no Triton or causal-conv1d and differ from CUDA reads by the same terms. Pair reads from the same kernel set. When a parent read is older than an image change, re-read the parent; a deployed app keeps the image of its last deploy.runs/drift-v1/probe_app.pyswaps the convolution and the delta rule in place to measure one change.
- Metric version 2 accepts whole equal-confidence groups.
paired_bootstrap(..., aggregation="micro")recomputes non-additive coverage/AURC for each paired record-group resample; defaultmacrois equal task weight. confident_error_ratedivides confident errors by all questions.error_rate_at_0_9divides by accepted questions. The empirical coverage-at-error envelope is not an unseen-data guarantee; freeze thresholds on a separate calibration set.- Local benchmarks save logits and effective inference temperature. Research studies explicitly score raw logits, fit on the calibration partition for both parent and candidate, and report raw/recalibrated metrics separately. Temperature can reorder confidence across multiclass questions, even at fixed option count.
- Registered screen:
experiments/calibration-audit-protocol.json;scripts/review_calibration_screen.py --study <name> --out runs/<new-review>verifies matched updates/tokens and gates against both unchanged parent and CE continuation. The screen ran and no candidate passed (round 3; PLAN.md Record, full text at tagresearch-archive-2026-09-24): the--label_smoothing/--brier_w/--focal_gammalosses exist but ship in nothing. evals/round3/transfer-r3/test.jsonlwas frozen as a fresh final panel; the loss screen found no qualifying candidate, so its first read was the 2026-09-22 release confirmation of the soft-target Kev-9B, and since round 11 it is half of the pooled short-state guard. It is spent as confirmation. Select and record a candidate before evaluating it; do not reuse previously inspected test partitions as untouched confirmation.- Isolate research deployments with
KEV_APP_NAME=kev-calibration-audit.worker_environmentpropagates app/GPU/secret-name settings to prevent Modal dependency-count startup failures; secret values stay in Modal Secrets. - Verify live spend/rates with
uv run modal billing summary --jsonanduv run modal billing rates --json. Training admission uses the actual configured CPU and maximum host memory, not the old 2-CPU/48-GiB assumptions. Initial cancelled startup calls and successful jobs are recorded separately.
- Use simple technical English. For README tone, use Jared's older Formik, TSDX, Razzle, and Backpack READMEs as references: explain the developer's problem, address the reader directly, and show code early. Avoid slogans, canned contrasts, and repeated claims. Keep detailed experiment history in PLAN.md and the model cards rather than repeating it in the README.
- README order is progressive: what Kev is, Highlights and the Models table (the first 30 seconds); Quick Start, Fine-Tune, Deploy and What to Expect
(the first 5 minutes); then Playground, API, How It Works, Training, Benchmarks, Serving Performance, Limitations. The README describes the current family
only. A release updates its Models row, the
MODELS/LOCKEDlists inscripts/plot_tweet.pyandscripts/plot_family.py(then regenerate) anddocs/claims.json; the dated release paragraph, the before/after numbers and the previous tag go in the model card and PLAN.md, not the README. - Pull requests teach. Write the body the way Andrew Clark (
acdlite) and Sebastian Markbåge (sebmarkbage) wrote theirs on facebook/react before 2023: the problem as it exists today first, then the mechanism as an argument with one concrete artifact per idea, then what is uncertain and what was left out. Define the one term the change hinges on (pointer head, temperature, flip rate, AURC, prefix cache) the first time it appears, so an engineer who is not an ML researcher can follow. Numbers carry their provenance (checkpoint, suite + partition, n, report path); the decision rule is written before the result. A list of files changed is the diff again, not a description. Length follows novelty: one sentence for a one-line fix, headings for a new loss or serving path. The title becomes the squash commit subject. Thekev-pr-descriptionskill has the reference PRs and a weak/strong pair.