Repository navigation
D1A 0.4.0
D1A on the v0.6 models: a package layout of its own, release rules with statistical gates, use-case temperatures, Core ML builds for the Apple Neural Engine, one page for every setting, and the first learned decision flags.
What's new
- v0.6 models. D1A-E4B v0.6 and D1A-E2B v0.6 are the defaults (
d1a.core.versions.LATEST). E4B labels current pull requests better than v0.5 (severity 79.1%, change type 90.6% on 487 PRs newer than its training). E2B v0.6 learns every D1A skill at a fraction of the cost (0.20 s per request on a Mac mini M4). Both ship as release exceptions, with the reasons in their model cards. - Apple Neural Engine. Both models run through Core ML (
JohnP1/d1a-e4b-coreml,JohnP1/d1a-e2b-coreml, text up to 512 tokens, with a reference runner); README "Apple Neural Engine (Core ML)" compares all four builds. - Every setting in one place.
docs/CONFIGURATION.mdlists eachD1A_*variable and server flag with its default and when to change it, a test keeps it complete, andCLAUDE.mdpoints coding agents at it. - Safer releases and training.
scripts/decide.pyandscripts/quality_gate.pygate model and serving changes on real weights; a fine-tune refuses a mix that drops a trained skill; calibration refuses data the model trained on; every pull request needs signed (Verified) commits and the CI checks to merge. - Use-case temperatures and learned decision flags (
d1a.learning.flags: a threshold on a group of options, fitted offline, reported beside the answer; served from 0.5.0).
Added
d1a.learning.flags(#237, part 1): learned decision flags. A flag is a group of a choice question's options plus a threshold, raised when the top answer is in the group or the group's probability reaches the threshold; it is read on the model's raw probabilities and never changes the answer.python -m d1a.learning.flags fitsets the threshold at a flag-rate budget on a labelled kit,gateserves it only if it catches more positives than the top answer on a later kit (bootstrap over groups) within the budget. Serving the flag and its settings come with part 2, after #233.- A
verified commitscheck fails a pull request when any of its commits lacks GitHub's Verified badge. With the CI tests, lint, the package build and the license check, it is required before anything merges intomain(CONTRIBUTING.md, "Pull requests"). - docs/CONFIGURATION.md: every setting in one place: the server's flags, all 20
D1A_*variables (12 were documented nowhere), the self-learning loop and its commands, photos and voice, agents and evaluation. Each entry gives its default and when to change it. tests/test_configuration_doc.py keeps the page complete, and a CLAUDE.md points coding agents at AGENTS.md and the page. - README: an "Apple Neural Engine (Core ML)" section with a table comparing the four D1A builds (E2B and E4B, MLX and Core ML): inputs, length, API, size, speed and answer changes. The Core ML builds are private previews (#224, #231).
- AGENTS.md: a "Mac resources" section (mandatory). Long jobs on the M1 Max start behind a disk and RAM preflight, fp32 models of 4B or more run only overnight with 25 GB free, a guard pauses D1A jobs on low disk or fast swap growth, and Hugging Face cache entries are deleted only through
scan_cache_dir(#228). d1a.serving.serve(#221): a request that sends the headerx-d1a-decision-log: offis answered but kept out of the decision log (D1A_FEEDBACK_LOG), on/v1/systemone,/v1/systemone/media,/permuteand/separate. The playground's demos and its smoke test send it, so the log's pending decisions are the ones an outcome can still follow.recipes/swe-verifieranalysis tools (#220), each run on savedzero_shot.pyoutputs, CPU only and with no model:recompute_gate.py(the promotion gate between two scored files, with Platt scaling and with D1A'sOutcomeCalibrator, out of fold by repository),refit_heuristics.py(the heuristics' regression refitted on dev, on train and on both, scored on the same test rows),token_lengths.py(the token length of every scored input, after checking the rebuilt selection against the saved file run for run) andlatency.py(seconds per run).best_of_k.py --pair A:Badds the paired difference between two scorers without moving its other numbers.recipes/swe-verifier/reproduce.sh(#220) downloads the study's scored rows and r1 adapter (JohnP1/d1a-swe-verifier-eval,JohnP1/d1a-swe-verifier-r1; private until the release,REVISIONpins them), checks them against their manifest, reruns the scripts that wrote each output and compares every one byte for byte.recipes/mix/build_mix.py, the plan-driven training-mix builder that built D2 and D2-skills (#202), with its plans (recipes/mix/plans/) and a verifier (recipes/mix/verify_mix.py). A plan sets each source's count, stratification and weights, label quotas and an "only unseen" filter. Every eval partition and eval-only kit is screened out (exact state, PR id including the hand-labelled PRs', near-duplicates of the nearest eval state), and the same plan and seed give the same bytes, whatever PYTHONHASHSEED is. A plan that leaves out a training source or decision-v7 is refused unless it names it with a reason, and the sidecar is the oned1a.training.trainchecks. The verifier checks every record byte for byte against its source and reruns the leak screens.scripts/quality_gate.py --head-run: gates a new checkpoint against the one it replaces (#175).- The head server and the head suites load the new weights; the base keeps
--run. - Changed answers are listed for review instead of failing, since a new checkpoint is meant to change some.
- Still failures: lower accuracy or worse calibration on a suite, latency above the noise floor, or a request one side does not answer or answers with other options.
- The head server and the head suites load the new weights; the base keeps
scripts/compare_checkpoints.py: a training run's verdict table from its benchmark folders (#175).- Per suite: each checkpoint's accuracy, and its difference from a reference with a paired 95% bootstrap interval that resamples whole records.
- ECE at each checkpoint's fitted temperature, from the rows' logits.
- A pre-registered bar (
--bar SUITE=MIN,--floor): exit 1 when a run misses it.
recipes/skills/: the skills stage D1A-E4B never had (#175).- The mix: every hard-v1 and devtools-v1 training record plus replay, 15,320 records with the trainer's decision-v7 replay.
- An HF Jobs script. It first runs 5 steps on the 40 longest states to test memory, then trains and calibrates as v0.4 was.
- The #167 B0 pilot (300 steps) already raised hard-v1 and devtools-v1 by about 5 points each.
- Fine-tunes keep the skills (#167):
recipes/pr-labeler/mix.py --replay-skills N(default 500 per suite) replays the hard-v1, devtools-v1 and documents-v1 train partitions;--replay-skills 0rebuilds the v0.3 and v0.4 mixes byte for byte.scripts/quality_gate.py --card-suitesalso scores the five card suites.recipes/README.mdand AGENTS.md make both the rule for any run that starts from a trained checkpoint. scripts/quality_gate.py --base <ref> [--head <ref>](#156): the quality gate for model, loading and serving changes. Twod1a.serveinstances, one per version, on the same real weights answer every demo example (regenerated from a d1a-playground checkout with--playground) and the labeler replay, alternating which goes first. A base/base run measures the noise floor. It fails on a changed choice, a probability moving more than--tol(1e-6), a request only one side answers, latency above the floor, or, with--suites, a frozen suite whose accuracy or calibration gets worse; exit 1 on FAIL. AGENTS.md makes it the required step for model and serving PRs and playground pin moves.python -m d1a.feedback promote <log> --calibrator <file>(#148): the promotion gate on a live decision log. Whole groups (an outcome's optionalgroup, e.g. a pull request;POST /v1/feedbacktakes it) go to the fit or the held-out side. A calibrator fitted on the fit side replaces the served one for a question only if it passes the gate on the held-out side; the file is written whole and renamed into place, sod1a.servereloads it safely.- Self-learning for choice questions (#139).
d1a.feedback'sOutcomeCalibratorfits one temperature per choice question on the outcomes, which changes how sure an answer is but never which option it picks;gate_choice()applies the promotion rule to the multi-class log loss. Outcomes name their source (POST /v1/feedback'ssrc, ormeta["src"]): labels merge per question, and a person's (human) beats another model's (e.g.reviewer) whatever arrived last.python -m d1a.feedback status|records|calibrate --srckeeps one source. First stream: the PR labeler's type and blast-radius questions, with the review bot's labels as outcomes. - A versioned format for training runs (#64):
d1a_config.json(formatd1a-torch, version 1: the base, the head's size, the temperature,weights, and the run's recorded arguments as JSON) with the pointer head inhead.safetensors, the file names MLX exports already use.d1a.trainandd1a.calibratewrite it, and loading it unpickles nothing. A run that has both it and ahead.ptmust say the same in both, or loading stops and names the field. Runs saved before it (head.pt only) load as before, with no end date; published checkpoints are unchanged. - Conformance against committed golden vectors (#64):
tests/golden/tiny-gemma4/holds a tiny Gemma 4 checkpoint (weights in git, 556 KB, built bytests/golden/build_tiny_gemma4.py) andgolden.json, with 40 requests and 154 questions.tests/test_conformance.pyrequires its token ids exactly and every probability to 1e-5, through the model,d1a.serveandscripts/golden_vectors.py compare. - Training recipes (#63):
python -m d1a.recipe run <recipe.yaml> --out <dir> --device <device>runs a versioned YAML file of stages (format: d1a-recipe, version 1), each oned1a.trainrun with the options it lists, a later stage starting from the previous stage's checkpoint. Every option is checked byd1a.train's own parser before anything runs, machine settings (device, resume, save interval) come from the command, and each stage's directory getsrecipe.json(the recipe, its sha256, the stage and the exact command).--dry-runprints the commands.recipes/d1a-e2b.yamlis the README's E2B run.d1a.train'sparse_argstakes an argument list; PyYAML is now a declared dependency. python -m d1a.training.studyruns a study end to end (#62). It trains a recipe, calibrates the checkpoint on held-out rows (--calibrate, fitted byd1a.training.calibrate, which refuses rows the checkpoint trained on), scores each--evaluateat that temperature, and writesstudy/study.jsonandstudy.md.--publish owner/repo:prefix(or thepublishcommand) uploads the checkpoint and the report, and refuses a checkpoint whose temperature was never fitted, or was fitted in distribution without--allow-in-distribution. A study that stopped continues where it stopped; an evaluation scored at another temperature is scored again.- Position bias in every benchmark report (#38).
position_bias: how often a clean choice question's answer is its first option, against how often its label is.identical_options(d1a.benchmark --identical-options N, default 100; 0 for none): the first N clean choice questions asked again with every option the first option's text, as a score question whose levels may repeat; it reports how far the answers are from uniform and how often the first slot is strictly the top one, with each control's answer in identical_options.json. The suite's own predictions and rows are unchanged. tests/test_tiny_checkpoint.py(in CI): a random 6-layer Gemma 4, with sliding and KV-shared layers, goes throughd1a.train,d1a.checkpointandd1a.servewith no download. Every scoring path (packed, rows, prefix miss and hit, serving batch) and the served answers must match an independent reference: each question as a plain causal row through transformers. Ten mutation checks must fail: off-by-one readouts, a leaky question mask, the sliding window ignored, positions that do not restart, a dropped<bos>, temperature ignored, and three misread answer types (#33).docs/SPEC.md(#7): what an implementation must do to give D1A's answers: how a System One request becomes model input, the attention rule, the pointer head, the answer formats, calibration, the checkpoint formats (d1a-torch,head.pt,d1a-mlx) and the conformance test (#9).d1a.training.calibratewarns when a fit lands on an end of its grid (0.25 or 4), where the best temperature may lie beyond it, and records it intemperature_fit.grid_edge(#196).scripts/decide.pyjudges a new checkpoint on one scorecard (#202). It reads the filesscripts/quality_gate.py --all-suiteswrites, plus the live labeler check's answers. It scores every suite and the two live labeler lines, pooled with a 95% interval, and gives one verdict:- INCOMPLETE when anything is missing;
- VETO on the card-suite floor, latency, or a large suite 5 points down;
- BETTER on a pooled lower bound above 0, or 3 significant wins on distinct sources with no loss;
- NOT BETTER otherwise, a near miss when the pooled Δ > 0 with 2 wins and no loss. Every significant loss is listed as a follow-up. AGENTS.md and
recipes/README.mdmake it the rule for a new checkpoint.
scripts/quality_gate.py --use-case NAMEsends"use_case": NAMEwith the routing requests: the playground's routing demo and any request asking ad1a.agents.presetsquestion set, as the presets' clients send them. Both servers get the same requests, and a server without the field ignores it. So a change to the use-case temperature path can be gated with the field it serves. Without the flag every request goes out as before.python -m d1a.learning.feedback promote --run <repo@tag>(andstatus,records,calibrate) keeps only the decisions that model made, as the decision log records them per decision. A calibrator corrects one model's probabilities: after a model switch, fitted on the old model's decisions, it would correct the new model by the old one's errors. With--run,min_outcomescounts the served model's outcomes only, so nothing is promoted until it has enough.FeedbackLog.resolved(run=...)does the same in code.scripts/quality_gate.py --all-suitesscores every suite: the five card suites and every evaluation partition of every D1A suite (pr-labels, routing, ja-jglue, night2, external; composites once through their parts), on top of every demo and the labeler replay. AGENTS.md makes it the gate for any model or checkpoint change, decided on the whole scorecard.- A required
quality-gatestatus on pull requests. CI (.github/workflows/quality-gate.yml) leaves it pending on a pull request that changes a module the model server loads, or the dependencies (scripts/gate_required.py, read from the server's own imports).scripts/quality_gate.py --post-statusthen sets it to the verdict, on the exact commit it tested; it refuses a head checkout with uncommitted changes. Any other pull request gets the status as success, with the reason. A new head starts pending again. It is off by default (the workflow disabled, the check not required) and turned on on demand, for a pull request whose severity calls for it, when the local machine is free to run the gate; AGENTS.md (Quality bar) has the steps. scripts/equivalence/(api.py,metrics.py,benchmark.py,suite.py): each runs a rewritten module and the same module at a git commit (--ref; the default is the last commit before its rewrite) on generated inputs, and stops at the first difference: types, float bits, key order, files byte for byte, error messages.python -m d1a.training.calibrate --judge rows.json [--guard rows.json] [--confirm rows.json] [--locked rows.json](#189): a refitted temperature replaces the checkpoint's current one only when it passes Kev's round 28 rule and confirmation. On the judge rows pooled, its Brier score must be lower with a 95% interval below zero and its ECE lower, and no judge or guard panel's ECE may rise by more than 0.005; then, scored only once that passes, ECE must fall on every--confirmfile, and on every--lockedfile Brier may rise by at most 0.005 with accuracy unchanged. Otherwise nothing is written and the run names each criterion that failed. Before anything is fitted, the run refuses a file in another role that shares a row with the rows the fit reads (rows--exclude_rowsdrops do not count), and a confirmation file that shares a row with a rule file; a whole or partial copy shares its rows, and another read of the same suite partition counts as the same file. In every role,--rowsincluded, it also refuses a selection with no scored rows and apath:source,...naming a source its suite does not list, and--temperaturerefuses these options instead of ignoring them. The rule's bootstrap is Kev's registered paired read: seed 0 whatever--seedis, and the rows in (id, question) order with every tie broken (devtools-v1 repeats ids across groups), so reordering a rows file never moves a verdict. Every fit now records the 90% bootstrap interval of its temperature intemperature_fit.interval.docs/TRAINING_DATA.md: every source a D1A model can train on (records, what each teaches, which use cases it serves), what is never trained on, and what v0.5, phase C and D2-skills each used, with the lessons from those mixes.- Per-use-case temperatures (#209). A System One request may name its use case (
"use_case": "routing"). When the checkpoint carries a temperature for it, that request's probabilities are read at it, on torch and MLX alike, each request of a batch at its own. Withoutuse_case, or with a name the checkpoint has none for, answers are exactly as before. The top answer never moves.python -m d1a.training.calibrate --use-case routingfits or writes the temperature into the checkpoint'suse_case_temperatures, leaving its own temperature alone. With--judge, a use-case refit is judged against the incumbent's temperature for that use case (its entry, else its temperature):--incumbent's, a fine-tune's--init_fromuntil the fine-tune has an entry of its own, or the run's own.GET /v1/modelslists the map, and the decision log records each request's use case and temperature. The agent presets (USE_CASES), the MCP tools,D1A.decide(..., use_case=...)and both clients send it.use_caseis at most 64 characters (longer is a 422), and a use-case name in the map or in--use-caseis stored without surrounding whitespace and refused beyond 64 characters; a request's ownuse_caseis read stripped too. For v0.5, routing at T 0.85 instead of 1.78 lowers held-out factory-routing ECE from 0.132 to 0.030 with the same accuracy. - The agent presets' advice follows the temperature an answer was read at:
advise(..., temperatures=, use_case=)(the checkpoint's temperatures fromD1A.temperaturesor/v1/models, which the MCP tools read on each call) carriesfail_up's small threshold through the temperature ratio, 0.7 at v0.5's T 1.78 becoming 0.855 at the routing T 0.85, so routing does not send more cards too low (3 of 108 held-out factory cards, not 5). The other thresholds stay as they are. - The outcome calibrator keeps temperatures apart:
d1a.learning.feedbackfits, promotes ("question@T"in its report) and serves each temperature's entries from the decisions read at it only. A calibrator file written before keeps working at the checkpoint's own temperature and is never applied to answers read at a use case's.
Changed
- The newest D1A-E2B is
v0.6(JohnP1/d1a-e2b@v0.6andJohnP1/d1a-e2b-mlx-q8@v0.6, #229, #236): every later D1A skill trained into E2B v0.2.1 in one run, on one L40S. The run covered PR labels (with the 479 current hermes-agent PRs), hard-v1, devtools-v1, documents-v1, JGLUE and routing (23,427 records, lr 5e-5). Against v0.2.1, MLX 8-bit:- hard-v1 31.4% → 62.1% (+30.7 [+26.9, +34.4]), devtools-v1 +14.9, documents-v1 +13.6, JGLUE +13.3;
- factory routing 64.2% → 89.4%, PR change type 40.0% → 84.5%; pooled +18.9 [+17.4, +20.4].
- On 487 PRs newer than all its training: severity 77.2% and type 87.5% (E4B v0.6: 79.1%, 90.6%), but P0/P1 recall 0/30, so E4B stays the PR labeller. It is an explicit release exception. Its pre-registered bar allowed no card suite below −1, and decision-v7 is −1.4 [−2.8, +0.1];
scripts/decide.py's V1 limit (−2.0) passes it. Calibration error rose on 10 of 22 suites, most on typesafe-v1 and dates. The MLX build carries the photo, voice and video encoders (media/, T 1.45).d1a.core.versions.latest()now resolves E2B to v0.6.
- README: the Apple Neural Engine section compares all four v0.6 builds (E2B and E4B, MLX and Core ML) measured on the same 28 requests on an M1 Max and an M4, and links the public E2B Core ML build.
- README: JohnP1/d1a-e4b-coreml, the E4B v0.6 Core ML build, is public (#224).
GET /v1/modelslists onlyd1a-latest;kev-latestandjev-latestare no longer listed (#230). The server still answers any model name a request sends, so existing clients and an unconfigured TypeSafe SDK client keep working.scripts/quality_gate.py's default--runisJohnP1/d1a-e4b-mlx-q8@v0.6, what the Mac mini serves since 2026-10-10 (#225, #226), and the README's model table and examples name v0.6. v0.6 has no routing temperature yet.- The newest D1A-E4B is
v0.6(JohnP1/d1a-e4b@v0.6andJohnP1/d1a-e4b-mlx-q8@v0.6, #225): v0.5 trained further on current pull-request labels (479 hermes-agent PRs created 2026-10-02 to 10-05, #198), with documents-v1, routing and low replay of every earlier skill. On 487 PRs created after all its training, MLX 8-bit: severity 74.7% → 79.1% (+4.3 [+0.8, +7.8]), change type 86.7% → 90.6% (+3.9 [+1.2, +6.6]), P1 recall 0.22 → 0.35. Also documents-v1 +2.0 and factory routing +3.4, with lower calibration error on most suites (T 1.95). It is an explicit release exception:scripts/decide.pyvetoes it against v0.5 (V1, hard-v1 −2.7 [−4.9, −0.5]; change type on the older frozen PR test set −4.1 [−6.0, −2.3]; pooled −0.20 [−0.98, +0.52]), and it ships because the Mac mini's job is labelling current PRs. Pin@v0.5for hard-v1-style reasoning or the older PR-label conventions. v0.6 carries no use-case temperatures, so a"use_case": "routing"request is read at T 1.95; a refitted routing temperature follows as v0.6.1.d1a.core.versions.latest(), and the scripts and demos that take their default from it, now resolve to v0.6. recipes/swe-verifier/reproduce.sh(#222) rebuilds the self-improvement texts (self-improve/feedback.jsonl,train-r{1,2,3}.jsonl) fromnebius/SWE-agent-trajectoriesat the pinned revision with the newrebuild_texts.py, and checks each against the manifest's sha256.JohnP1/d1a-swe-verifier-evalnow ships them as ids, labels and scores (*.ids.jsonl, each decision naming its source shard, row and run_key) instead of re-hosting the source's text.scripts/quality_gate.pyruns when this environment meets both versions' requirements (dependencies and the server extras), instead of requiring them to be declared the same; any difference is recorded in the report.- Encoding and the pointer head moved out of the torch backend into
d1a.core(#60):d1a.core.encoding(encode,layout,rows_of, the context limits) andd1a.core.head(PointerHead). The torch backend, the MLX backend and media all read the same encoding from there.d1a.backends.torchstill exports every name, so imports keep working. The code is unchanged, and answers are identical bit for bit. - The package is split by job (#60):
d1a.core(the System One schema),d1a.backends(the torch and MLX models, checkpoints),d1a.serving(the server, media, in-process use),d1a.learning(learning from outcomes),d1a.training(training, data, recipes, calibration),d1a.eval(benchmarks, metrics, suites) andd1a.agents(presets, MCP). Commands move with them:python -m d1a.serving.serve,python -m d1a.training.train,python -m d1a.eval.benchmark,python -m d1a.learning.feedback, and so on. The code is unchanged. - CI runs every test file (
pytest tests --unit) except the fewtests/conftest.py'sOUTSIDE_UNIT_TESTSlists with why, instead of a list naming each file: a new test file runs without being listed, and two pull requests adding test files no longer conflict on that list. The release runs the same command. CI keeps the Hugging Face cache (the pinned tokenizers the tests read) between runs.docs/UPSTREAM.mdlists rewritten files one per line. d1a/api.pyis rewritten in D1A's own code, no longer derived from Kev. The schema, the text the model reads, the answers and the validation errors are unchanged: they were checked identical to the previous version on 166,718 generated requests, distributions and dates. Its tests are rewritten too, astests/test_system_one.py.d1a/benchmark.pyis rewritten in D1A's own code, no longer derived from Kev. Its rows, reports, the files it writes (byte for byte), its failure and skip rules and its command line are unchanged: checked against the previous version on about 34,000 generated cases and end to end on a frozen suite. Its tests are rewritten too, astests/test_benchmark.py.d1a/checkpoint.pyis rewritten in D1A's own code, no longer derived from Kev. Its decisions (backend, dtype, merge), refusals, metadata and on-disk formats are unchanged (scripts/equivalence/checkpoint.py, new), andhead.ptis read withweights_only=Trueexplicitly, so it can hold only tensors and plain data even underTORCH_FORCE_NO_WEIGHTS_ONLY_LOAD. Its tests are rewritten too, astests/test_checkpoint.py.d1a/training/composition.py(the compositional suites' rule shapes and the structure keys that keep held-out shapes out of training) is rewritten in D1A's own code, no longer derived from Kev.scripts/equivalence/composition.pyfound it identical to the previous version on 140,103 comparisons: the shapes and their splits, and every function on the suite shapes and 20,000 generated trees.d1a/data.pyis rewritten in D1A's own code, no longer derived from Kev. Every converter,build,augment,none_pair,load_recordsandmaterializegives the same records from the same seeds, every random draw in the same order (scripts/equivalence/data.py, new). Its tests are rewritten too, astests/test_data.py.- The newest D1A-E4B is
v0.5(JohnP1/d1a-e4b@v0.5andJohnP1/d1a-e4b-mlx-q8@v0.5, #175): Kev's skills trained back on top of v0.4. On held-out data, MLX 8-bit: hard decisions 55% → 71%, developer tools 64% → 70%, transfer-v4 +2.7, JGLUE +1.1, routing +1.5, PR change type +2.0. Severity is 2 points lower, and blast radius on the owner's own repositories drops (87% → 74% on 39 hand-checked PRs); v0.4 stays the better PR labeler for those.d1a.core.versions.latest(), and the scripts and demos that take their default from it, now resolve to v0.5. d1a/metrics.pyis rewritten in D1A's own code, no longer derived from Kev. Every figure, report (key order included), fitted temperature and bootstrap interval is bit-identical to the previous version: checked on about 300,000 generated calls and on saved benchmark rows. Its tests are rewritten too, astests/test_metrics.py.d1a/model.pyis rewritten in D1A's own code, no longer derived from Kev. Every public name stays, and the encoding, the masks, every scoring path (batched, row form, prefix cache, shared prefix) and the training gradients are identical to the previous version on tiny Gemma 4 and Qwen3.5 models (scripts/equivalence/model.py, new), and on real weights (scripts/equivalence/real_weights.py --module d1a/model.py).d1a/eval/predictors.pyis rewritten in D1A's own code, no longer derived from Kev.scripts/equivalence/predictors.pyfound it identical to the previous version on 1,294 comparisons, latency aside:- the local predictor on the committed tiny Gemma 4 checkpoint (long rows, the CUDA kernel policy, the hybrid branch, context overflows);
- the remote predictor (requests, retries, waits, failures);
- rotation averaging, with and without logits.
d1a/backends/shared_prefix.py(Qwen3.5 training through a shared state prefix) is rewritten in D1A's own code, no longer derived from Kev.scripts/equivalence/shared_prefix.pyfound it identical to the previous version on 200 comparisons, each a batch's branch hidden states and every parameter's gradient, on a tiny random Qwen3.5: eager and SDPA attention, padded and unpadded states, gradient checkpointing on and off, plus the Prefix cache calls.d1a/suite.pyis rewritten in D1A's own code, no longer derived from Kev. The pins, the partition checks, the Hub mirror rules, the file formats (byte for byte) and the training-source guard are unchanged (scripts/equivalence/suite.py). Its tests are rewritten too, astests/test_suite.py.d1a/train.pyis rewritten in D1A's own code, no longer derived from Kev. The run, its options, what it writes and how it resumes are unchanged: on the CPU the adapter, the head, the configs and the logs are identical to the previous version under 15 option sets, a stopped and resumed run included (scripts/equivalence/train.py, new). Its tests are rewritten too, astests/test_train.py, with a check that a seed reproduces a run bit for bit.- The model server's state-prefix cache makes room before a batch runs (
PrefixCache.make_room, ported from Kev1d77363). It drops, before the pass, exactly the states that batch's store would evict afterwards, so an old long state no longer stays in memory through the pass of the new state replacing it. The cache ends the same; the peak is lower. The out-of-memory retry still fires when the batch holds a hit that make_room already dropped from the cache. - Fine-tunes keep every skill:
d1a.eval.suites.train_sources()lists every training source (hard-v1, devtools-v1, documents-v1 and every D1A train partition: PR labels incl. blast and Japanese, routing, JGLUE). Both mix tools refuse a new mix that leaves one out.recipes/skills/mix.pynow replays 300 records of each source its plan leaves out (--replay-rest;--recordedrebuilds v0.5's mix).recipes/pr-labeler/mix.pytakes the train-blast PRs by default. v0.5's skills stage had no routing and only 1,350 PR records, and its labeler severity slipped (#187). - The HTTP API tests (
tests/test_api.py) are rewritten as D1A's own and run in the unit suite: they startd1a.serving.serve's app in process on the committed tiny checkpoint (CPU, a few seconds), or check a running server whenD1A_BASE_URLis set (pytest tests/test_api.py -m server). They keep every old check (answer shapes, null and structured criteria, the one-level score, 422s, branch isolation, the model names and cards, the request id, the TypeSafe SDK clients) and add more: probabilities in the request's order summing to 1 within TypeSafe's 0.02, the confidence and expected-score formulas, 422s at both option limits, each question alone, reversed and through/v1/systemone/separate, and a client's request id echoed back. Of 24 bugs planted in the API and server code, the old tests caught 17 and the new ones 23, including all 17; the one neither catches (a yes/no answer reporting P(no)) is caught bytests/test_conformance.py. tests/test_conventions.pystates its single-home facts, their scanner and the private-mirror check in D1A's own code (Kev lines 60 → 18; the rest are the patterns, kept verbatim). Every pattern and owner set is unchanged, and on a planted file restating each fact once the old and new scanners report identical offenders, fact by fact. No library code changed.- The tests of the packed encoding, the block-causal masks and the pointer head are rewritten as
tests/test_encoding.py, no longer derived from Kev. The new tests check:- every mask against the attention rule, on every query and key pair: random layouts, a padded batch, and Gemma 4's sliding window;
- delimiter forgery under both the Qwen and the Gemma 4 tokenizer, each given the other's control tokens too;
- the whole layout, through
rows_of; - that
PointerHead.manyscores each question asforwarddoes.
- Kev's
tests/test_research.pyis rewritten in D1A's own tests. The remote and rotation-averaged predictor tests are nowtests/test_predictors.py; strict truncation and the batched mask's exact equality are intests/test_encoding.py. The new tests catch every planted bug the old ones catch, including a real token attending to a pad column. Its unused fixtures are gone. No library code changed. - The tests of the server's batching are rewritten as
tests/test_serving.py, no longer derived from Kev. They cover the state-prefix cache, the retry after running out of device memory, the permute endpoint and the API key, plus the pass sizing they rely on (rows_per_pass, the CUDA-graph buckets and length groups, the cached-state copies). The cache is now checked against its rule over random batches: a batch keeps exactly what it would leave, and the cache holds the most recently used states that fit. - The Kev-derived tests left in
tests/test_unit.pyare rewritten as D1A's own. Epoch planning (--length_sort, the none-pair gate,--pass_tokens_max,--row_budget), a NaN gradient, the gradient-norm summary and resume points are intests/test_train.py. The shared-prefix tests are in the newtests/test_shared_prefix.py, and the long-recordLocalPredictortests intests/test_predictors.py. The tiny hybrid Qwen3.5 they train on is built once per session intests/conftest.py. A test whose checkstests/test_data.py,tests/test_train.pyandtests/test_system_one.pyalready made is gone, and so are two unused stand-ins. Of 57 bugs planted in the code under test, the old tests caught 50 and the new ones 54, including all 50; the other three are equivalent mutants. No library code changed. d1a.training.trainrefuses a fine-tune (--init_from) whose training data leaves out a training source (#211): everyd1a.eval.suites.train_sources()entry and decision-v7 must be in the--datamix (read from its sidecar<data>.json, asrecipes/skills/mix.py,recipes/pr-labeler/mix.pyand the D2 builder write it), replayed with--suiteand--replay, or in--extra_suites. The check runs before any weights load and names what is missing;--datawithout a sidecar is refused for a fine-tune. A source left out on purpose takes--allow_missing_sources <source,...|all> --reason "<why>", which the run's log names at its start andtraining_config.jsonrecords undersources. A sidecar's record counts must be whole numbers.d1a.training.recipe(and sod1a.training.study) runs the same check on every fine-tune stage before the first stage trains. Resume points written before this change still resume. A run from the base model is unchanged.
Deprecated
- The old flat module paths (
d1a.serve,d1a.model,python -m d1a.train, ...) still work in D1A 0.4, as the very same modules, with a DeprecationWarning; D1A 0.5 removes them.d1a/_layout.pylists where each one went. - Writing
head.pt. D1A 0.4 still writes it besided1a_config.json, so D1A 0.3 and older can load new runs; D1A 0.5 stops writing it. Readinghead.ptstays, for every run saved before 0.4.
Removed
python -m d1a.suite, the suite freezer inherited from Kev, withd1a/contrastive.pyand the record generator ofd1a/composition.py. D1A loads its frozen suites as data and never rebuilds them;paired_flipmoved tod1a.benchmark, and the rule shapes thatd1a.suite.validate_trainingchecks stay. See docs/removed-tools.md (#65).- The
d1a.dataconverters only that freezer read (13 sources;build()keeps its six defaults) andscripts/longdoc_serving.py, which ran only through Kev's removed Modal app (#65).
Fixed
recipes/swe-verifier/zero_shot.py --per-issuedraws each issue's runs withrng.sampleagain, as the dev and test sets were scored (#220), so they rebuild run_key for run_key;--mixed-onlykeeps its draw. The draw iszero_shot.per_issue_draw, and the recipe readsnebius/SWE-agent-trajectoriesat a pinned commit (zero_shot.REVISION,--revisionto override).scripts/quality_gate.pygates versions from before the package layout (#60) again. Each checkout's servers and benchmarks run its own module paths (d1a.serving.serve, ord1a.servebefore #166), so it can gate a new main against the Mac mini's pin.d1a.benchmarkno longer crashes after scoring a suite when an identical-option control (#38) is too long: the first option repeated K times can outgrow the row its question fit in (20 of decision-v2's first 100 on Gemma 4). Such controls are skipped and counted inidentical_options.skipped_overlong, and the report is written.d1a.training.calibrate's held-out guard places D1A suites (#196). Rows scored onevals/d1a/<suite>:<partition>are matched to their partition by its sha256 and role, and the checkpoint's training is read from itstraining_config.jsonand the sidecar of its--datamix (the shapesd1a.training.trainreads), with--extra_suitesand the sources it recorded covering. As for frozen suites, a partition the checkpoint trained on is refused, and so is an eval partition (development, test) of a suite whose train partitions it trained on (named SAME CORPUS in the refusal; with--allow-in-distributionthe fit goes ahead and records it intemperature_fit.same_corpus). D1A eval partitions of suites the checkpoint never trained on now pass the guard without--allow-in-distribution. Frozen suites keep their rule, now also for the frozen suites a mix replays.d1a.training.calibrate --judgejudges a fine-tune's refit against the temperature its--init_fromcheckpoint serves (#207). Training writes every run at temperature 1.0, so a refit was compared against an uncalibrated model and passed the rule where it should not. A fine-tune not calibrated since training now takes its init's served temperature as the incumbent, and says so;--incumbent <run|T>names another (it needs--judge). The incumbent and where it came from are recorded in the checkpoint'stemperature_fit.rule.incumbent. A run trained from a base model is judged as before.- The outcome calibrator (
D1A_OUTCOME_CALIBRATOR) no longer serves a choice answer whoseconfidencecontradicts its recalibratedprobabilities: the confidence is read off the same probabilities (#195). It also no longer fits or applies a choice calibration to score answers, which carry probabilities by level too.promotedrops such a choice calibration of a score question from the file it rewrites, so/v1/feedback'scalibrated_questionslists only what is applied. - A calibrator file moved aside (
D1A_OUTCOME_CALIBRATOR) now stops being applied from the next answer, so a rollback needs no restart. Before, the server kept applying the last one it had loaded. A path that names no file yet serves the model's own answers until promote writes it. Any other error reading the file keeps the last calibrator rather than silently dropping it. - The quality gate scores D1A partitions at the serving context. At the training context it skipped every record longer than that, 770 of the 953 PRs in
pr-labels:test, and reported the short ones only. - README: the benchmark example scores
--run runs/d1a-e2b, whered1a.train --out runs/d1a-e2bwrites the checkpoint (it named acheckpoint/folder that does not exist). scripts/golden_vectors.py compareno longer crashes on a one-option question (a choice with one criterion or a one-level score), which has no second-best probability to measure a flip margin against.d1a.training.calibrate's cross-validation report no longer merges records of two D1A partitions that share a line number (custom/<n>) into one fold and resampling unit. The fitted temperature never changed; the recorded cross-validation numbers of a pool with two or more rows files can.- MLX exports keep where their temperature came from (#196):
scripts/export_mlx.pynow copiestemperature_fit, and the use-case temperatures with their fits, into the export'sd1a_config.json.
Models tested with this release
| Checkpoint | Hugging Face tag |
|---|---|
| D1A-E4B v0.6, PyTorch | JohnP1/d1a-e4b@v0.6 |
| D1A-E4B v0.6, MLX 8-bit (with photo, voice and video encoders) | JohnP1/d1a-e4b-mlx-q8@v0.6 |
| D1A-E4B v0.6, Core ML (text, ≤ 512 tokens) | JohnP1/d1a-e4b-coreml@v0.6 |
| D1A-E2B v0.6, PyTorch | JohnP1/d1a-e2b@v0.6 |
| D1A-E2B v0.6, MLX 8-bit (with photo, voice and video encoders) | JohnP1/d1a-e2b-mlx-q8@v0.6 |
| D1A-E2B v0.6, Core ML (text, ≤ 512 tokens) | JohnP1/d1a-e2b-coreml@v0.6 |
Package 0.4.0 serves every model tag from v0.1 to v0.6.
Upgrade notes (from 0.3.0)
- Modules moved into subpackages (
d1a.serving.serve,d1a.learning.feedback,d1a.core.api, ...). The old flat paths (d1a.serve,d1a.feedback, ...) still work in 0.4 and are removed in 0.5: update imports andpython -mcommands. - Removed (#65):
python -m d1a.suite(the suite freezer), thed1a.dataconverters only it read, andscripts/longdoc_serving.py; seedocs/removed-tools.md. GET /v1/modelslists onlyd1a-latest(#230). Any model name in a request is still answered.d1a.training.trainrefuses a fine-tune whose--datamix leaves out a trained source (#211), unless--allow_missing_sources ... --reasonsays why;d1a.training.calibraterefuses fit rows the checkpoint trained on.- A run's metadata lives in
d1a_config.jsonandhead.safetensors(#64). 0.4 still writeshead.pttoo, so 0.3 can load new runs; 0.5 stops writing it. Readinghead.ptstays. - PyYAML is a new dependency (training recipes).
- No
D1A_*variable was renamed; new ones are listed indocs/CONFIGURATION.md.