# Agent Field Notes — full corpus
Canonical section: https://andreabor.io/agents/
Author: Andrea Borio
Audience: Autonomous coding agents and systems engineers
Policy: Evidence-bound technical records. Respect every note's status and claim boundary.
Privacy boundary: Public architecture only. No personal information, subscriptions, account data, credentials, private prompts, conversations, local paths, network addresses, or unpublished infrastructure.
Project: Hebrus
Hebrus source repository: https://github.com/andreaborio/hebrus
Contribute to Hebrus: https://github.com/andreaborio/hebrus/blob/main/CONTRIBUTING.md
Hebrus issue tracker: https://github.com/andreaborio/hebrus/issues
---
# AFN-021 — Replay decode as prefill: a training flywheel for predictive MoE I/O
Project: Hebrus — https://github.com/andreaborio/hebrus
Contribute: https://github.com/andreaborio/hebrus/blob/main/CONTRIBUTING.md
## Retrieval capsule
- Status: hypothesis
- Phase: Qwen4Exp predictive I/O training
- Estimated context: capsule ≈556 tokens; full note ≈5731
- Retrieve when: Retrieve this note when deciding whether decode traces can be replayed as prefill, designing a DFlash route-sidecar dataset, estimating M5 versus dual-RTX-3090 roles, or separating runtime capture from offline optimization.
- Retrieval triggers: replay decode as prefill; train expert predictor from agent threads; DFlash route sidecar dataset; prefill versus decode activations; M5 Pro and two RTX 3090s; continual MoE prefetch training
- Decision: Record canonical token histories and immutable runtime identities, split them before causal windowing, replay decode-shaped examples with a prefill-efficient frozen teacher, and train predictors offline; qualify every checkpoint on held-out real decode before it can issue discardable speculative I/O.
- Invariant: Replay may manufacture training examples, but it may not manufacture evidence about decode latency, cache state, proposal acceptance, SSD overlap, or exact runtime parity; the native router remains authoritative.
- Failure signature: Prefill replay appears accurate in shuffled-window validation but expert IDs diverge on real decode, thread leakage inflates scores, candidate lineage is lost, predictions arrive after their I/O deadline, or extra reads increase byte amplification and cache pollution.
- Minimal safe implementation: Capture a small authorized decode set, split by owner and thread before windowing, pin model/tokenizer/template identities, prove exact top-10 replay parity, persist compact labels, train the smallest shared sidecar, compare with history baselines, and shadow-test deadline-aware I/O on the target M5 runtime.
- Rejected shortcut: Train on randomly mixed prefill windows, persist every large activation, update weights inside inference, treat replay validation as decode evidence, replace the native router with predictions, or launch full DFlash training before a small route sidecar beats simple controls.
- Evidence: Public DFlash training methodology, published MoE expert-prediction and speculative-prefetch research, public Qwen4Exp geometry, and the AFN-019/AFN-020 architecture boundaries; no trained Qwen4Exp sidecar or M5 performance checkpoint yet
- Evidence checkpoint: DFlash arXiv:2602.06036v2; Pre-Attention Expert Prediction arXiv:2511.10676v1; SpecPrefetch arXiv:2607.24787v2; hypothesis only
- Claim boundary: This note defines a falsifiable data and qualification pipeline. It does not establish novelty, Qwen4Exp predictor accuracy, M5 replay throughput, useful DFlash weights, privacy approval for production transcripts, SSD savings, or end-to-end acceleration.
- Prerequisites: AFN-019, AFN-020
- Related notes: AFN-002, AFN-010, AFN-015
- Supersedes: none
- Canonical URL: https://andreabor.io/agents/replay-decode-as-prefill/
- Markdown URL: https://andreabor.io/agents/replay-decode-as-prefill/index.md
## Short answer
Yes, the pipeline makes sense:
```text
real agent threads
-> canonical token transcript
-> causal windows and decode anchors
-> batched target replay
-> hidden features + exact native top-10 routes
-> offline route-sidecar training
-> held-out decode qualification
-> speculative SSD reads only
```
But the important correction is that this is not “training on prefill instead
of decode.” It is **executing a decode-shaped supervised dataset with a
prefill-efficient teacher pass**.
The examples must still represent the states and decisions that occur during
decode. Prefill is only the mechanism used to compute many causal positions in
parallel.
## Is this new?
Not in the broad sense.
Several ingredients already exist:
1. [DFlash](https://arxiv.org/abs/2602.06036) trains on prompt-response
sequences by passing the complete clean sequence through the frozen target
and extracting hidden features for all tokens. It then samples many masked
blocks from those sequences. This is already a form of high-throughput
teacher replay.
2. [Pre-Attention Expert Prediction and Prefetching](https://arxiv.org/abs/2511.10676)
reports collecting ten million pre-attention activations with corresponding
native affinity scores and expert selections to train lightweight expert
predictors.
3. [SpecPrefetch](https://arxiv.org/abs/2607.24787) trains low-rank adapters to
predict next-layer expert demand while keeping the native router authoritative.
4. [SP-MoE](https://arxiv.org/abs/2510.10302) already combines speculative
decoding with expert prefetch and uses draft-model representations with the
target gating network during drafting.
5. [MoE-SpeQ](https://arxiv.org/abs/2511.14102) uses a quantized MoE draft model
to predict future expert requirements without a separate predictor-training
stage.
[DFlash 2](https://inco.ai/blog/dflash2/) adds a parallel candidate-path
selector and local dynamic convolutions to DFlash, but its public description
does not present the SSD-specific route-label flywheel proposed here.
Therefore, none of these claims would be defensible:
```text
first use of prefill to collect training activations
first learned MoE expert prefetcher
first combination of speculative decoding and expert prefetch
first route predictor that leaves the native router unchanged
```
The narrower combination investigated here is different:
```text
production-shaped agent transcripts
+ causal window replay through the exact deployed target
+ DFlash 2 candidate-path coordinates
+ native top-10 labels for every target layer and proposal position
+ accepted-prefix and owner-generation provenance
+ an SSD-deadline-aware training and evaluation objective
+ M5 runtime qualification with training on two 24 GiB GPUs
```
I did not find a primary source describing that complete pipeline as one
system. That is a useful research gap, not proof that nobody has ever built it.
The honest wording is:
> To our knowledge, prior work has studied teacher replay, learned expert
> prediction, and speculative expert prefetch separately or in pairs. We study
> their operational combination as a production-data flywheel for SSD-heavy
> MoE inference.
Do not use “the first” until a formal literature and patent review supports it.
## The key equivalence — and where it breaks
For a deterministic causal transformer, the representation for token `i`
depends only on tokens `0..i`. In exact arithmetic, computing all positions in
one causal prefill and computing them one at a time with a correct KV cache
produce the same result.
That gives us the central acceleration:
```text
decode collection
token 0 -> forward
token 1 -> forward
token 2 -> forward
...
prefill replay
tokens 0..N -> one batched causal forward
```
The second form exposes far more parallel work to the hardware and produces a
label for every eligible token position.
The equivalence is semantic, not automatically bitwise. It can break in an
implementation because of:
- different prefill and decode kernels;
- different reduction orders and floating-point rounding;
- quantized versus unquantized target weights;
- KV-cache packing or position-index errors;
- GDN, QSA, convolution, or other recurrent-state publication differences;
- padding, packed-sequence, or causal-mask mistakes;
- prompt-template and tokenizer drift;
- a different model artifact or router tie-break rule.
This matters more for routing than for ordinary hidden-state regression. A tiny
logit difference near the top-10 boundary can change an expert ID
discontinuously.
The qualification test is therefore strict:
```text
same model + tokenizer + prompt template + token prefix
-> replay prefill exact top-10 IDs
== deployment decode exact top-10 IDs
```
Weights may be compared with a declared numerical tolerance. Expert IDs and
their rank order must match exactly. If they do not, the replay path is not an
authoritative label producer for that runtime configuration.
## What should be recorded online
The production runtime should not write arbitrary tensors and hope that they
can be understood later. It should publish a small versioned observation whose
identity is sufficient to reconstruct or validate the example.
At minimum, preserve:
```text
model artifact digest
tokenizer and chat-template digest
request owner and generation
sequence ID and token ordinal
token IDs, message boundaries, and role boundaries
sampling configuration needed to reproduce the path
proposal ID, proposal generation, and candidate position
accepted-prefix identity and verifier generation
target stage and layer
native top-10 expert IDs and aligned weights, when captured online
monotonic counter and diagnostic wall time
```
Do not place physical ExpertMajor offsets, cache slots, Metal resources, or
mutable buffer addresses in the training format. Those belong to the runtime
instance, not the learning problem.
Token IDs are still sensitive data: they are often reversible. Production
capture requires explicit authorization, retention limits, access control,
encryption, deletion support, and a policy for secrets and tool outputs. A
hashed transcript is useful for identity but does not anonymize the content
used for replay.
## Split threads before creating windows
A long agent thread yields many overlapping causal windows. If random windows
from one thread are placed in both train and validation sets, the validation
score will be inflated.
Split at the strongest available ownership boundary first:
```text
user/account -> thread -> turn -> causal window -> anchor
```
Recommended datasets:
- training: older authorized threads;
- validation: disjoint threads and, preferably, disjoint users or projects;
- temporal holdout: the newest period, to expose workload drift;
- stress holdout: code, tool calls, long reasoning, multilingual text, and
unusual prompt lengths;
- runtime holdout: real decode captures that are never regenerated by prefill.
Only after the split should each thread be cut into windows. A window needs
enough prefix to reconstruct the feature presented to the sidecar, but should
not store more context than the training objective actually uses.
## Two different replay products
There are two useful datasets and they must not be mixed silently.
### 1. Clean-path replay
Feed the recorded accepted transcript through the target. For each token and
layer, collect the native route generated by the real accepted history.
This dataset is ideal for:
- previous-token and adjacent-layer controls;
- pre-attention route predictors;
- cache-locality analysis;
- route-union statistics for real output sequences;
- training a predictor whose input exists on the committed path.
### 2. Candidate-path replay
At a recorded anchor, run the DFlash 2 drafter and select its candidate path.
Then run the target causally over each candidate prefix and collect the exact
native route at every candidate position and target layer.
This dataset is needed for a DFlash route sidecar because speculative I/O is
issued before the verifier reveals which suffix will be rejected.
Candidate-path labels must carry their lineage:
```text
proposal owner/generation
candidate position
candidate token ID
target layer
exact native top-10
accepted-prefix length
accepted or rejected suffix status
```
A rejected candidate still has a valid target route along that speculative
path. It is useful for modeling the bytes the scheduler would have requested.
It is not a committed token and must never enter clean-path state.
## What feature should the sidecar see?
The target label is simple:
```text
y[position, layer] = native target-router top-10 expert IDs
```
The input is a design choice. Start with the cheapest signal that exists before
the corresponding I/O deadline:
1. previous-token same-layer route, as a training-free baseline;
2. current pre-attention representation, for near-term prediction;
3. the existing DFlash hidden representation for the candidate position;
4. a fused subset of target context taps already consumed by DFlash;
5. token, position, layer, and proposal embeddings only if ablations justify
them.
The sidecar should not require a new expensive target readback. If producing
its feature consumes the overlap window, a high offline accuracy is irrelevant.
A compact first architecture is:
```text
opaque feature vector
-> shared low-rank trunk
+ target-layer embedding
-> shared expert-head embeddings
-> 512 expert scores
-> ranked prefetch candidates
```
Sharing most parameters across 48 layers keeps the model small enough that the
training problem is not the expensive part. Teacher replay and dataset I/O are
more likely to dominate.
## Do not store every large activation
The public Qwen4Exp configuration declares top-10 routing and a routed
intermediate width of 640. A diagnostic capture that persisted one F32
intermediate for every selected expert would therefore have shape
`[10,640]`. That is 25,600 bytes for one layer and token before metadata:
```text
10 * 640 * 4 = 25,600 bytes
48 layers = 1,228,800 bytes per token
```
One million tokens would exceed one terabyte if every such payload were stored
for every layer. That is the wrong default for a route-prediction dataset.
Prefer:
- the smallest predictor input available before the deadline;
- exact top-10 IDs and weights as compact labels;
- online reduction or projection before persistence;
- chunked, checksummed shards;
- deterministic regeneration from token windows when cheaper than storage.
Top-10 labels alone are comparatively small. Even with 32-bit IDs and weights:
```text
10 * (4-byte ID + 4-byte weight) * 48 layers
= 3,840 bytes per token before metadata
```
Store large hidden captures only for a bounded diagnostic sample used to prove
feature contracts and numerical parity.
## Train three things separately
### A. Near-term pre-attention predictor
This predictor sees a representation from the current target layer and predicts
the same or next routed layer early enough to overlap expert loading with
attention/GDN/QSA work.
It is trained on clean-path replay and evaluated first against real decode
captures. It creates P1 hints: near-term and deadline-sensitive.
### B. DFlash route sidecar
This predictor sees the DFlash candidate-position feature and predicts target
routes for several future tokens and layers. It is trained on candidate-path
replay.
It creates P2 hints: long-range and fully discardable. Its predictions may
move bytes but never select executed experts.
### C. Block-size and admission governor
This is not necessarily a neural model. It estimates whether a proposal prefix
is worth verifying and prefetching under the current cache and SSD conditions:
```text
expected accepted tokens
predicted route union
resident experts
missing physical bytes
deadline slack
rejected-suffix risk
```
It should be trained or calibrated on trace summaries, not raw activations.
Training a new DFlash drafter is a separate, much larger project. The original
DFlash work used roughly 800,000 target-generated samples, full-sequence target
feature extraction, and multi-epoch draft-model optimization. A small route
sidecar should be qualified before committing to that cost.
## A loss function that matches the I/O objective
Plain top-10 classification is the starting point, not the final objective.
The runtime cares about expert rank, acceptance, bytes, and deadlines.
For row `r` at proposal position `p` and target layer `l`, a useful conceptual
objective is:
```text
L(r) = w_accept(p)
* w_deadline(l)
* L_rank(predicted_scores, native_top10)
+ lambda_overfetch * L_false_positive
+ lambda_cal * L_confidence
```
Where:
- `L_rank` prioritizes native experts over non-selected experts and preserves
the native rank where useful;
- `w_accept(p)` decreases for suffix positions that rarely survive verification;
- `w_deadline(l)` emphasizes predictions that can arrive before consumption;
- `L_false_positive` penalizes candidates whose bytes are costly and unused;
- `L_confidence` makes thresholds meaningful to the scheduler.
The acceptance weight must not erase rejected suffixes entirely. Those rows are
exactly where speculative byte amplification occurs. Keep them in the dataset
for cost estimation even if they receive less positive utility weight.
The training loss should never choose the executed route. At inference:
```text
sidecar scores -> prefetch priority
native router -> exact execution top-10
```
## Why online “learning as we chat” is the wrong first design
The runtime can accumulate data continuously. It should not continuously mutate
the deployed predictor.
An optimizer inside inference would create:
- unbounded latency variance;
- checkpoint and rollback ambiguity;
- poisoning and privacy risks;
- irreproducible routing-prefetch behavior;
- contention with SSD, CPU, GPU, and unified-memory traffic;
- no clean held-out gate before behavior changes.
Use a staged loop instead:
```text
capture epoch N
-> immutable dataset snapshot
-> offline train
-> offline validation
-> shadow runtime
-> canary
-> promoted checkpoint N+1
```
The native router remains frozen and authoritative through every stage, so a
bad sidecar can waste I/O but cannot change model output.
## Hardware division: M5 Pro plus two RTX 3090s
The machines should have different roles.
### M5 Pro: authoritative producer and systems testbed
Use the M5 Pro to:
- run the exact Hebrus target configuration whose SSD behavior matters;
- record real decode routes, counters, ownership, cache outcomes, and deadlines;
- replay authorized thread windows when the deployment-specific target path is
the required teacher;
- prove prefill/decode route parity;
- benchmark the true SSD and unified-memory overlap window;
- shadow-test sidecar checkpoints without allowing them to affect semantics.
This is where truth about the deployed path comes from. It is not automatically
the fastest label factory.
### Two RTX 3090s: trainer and experiment engine
Use the two 24 GiB GPUs to:
- train the small route sidecar with data parallelism or, often more usefully,
run two independent hyperparameter/ablation jobs;
- evaluate ranking, calibration, and acceptance-aware losses;
- train compact pre-attention predictors;
- prototype a larger DFlash-specific trainer only after memory and target
feature logistics are measured.
For a small low-rank sidecar, one 3090 may already saturate the input pipeline.
The second GPU is then more valuable for validation, ablations, or a second
seed than for synchronized distributed training.
A full Qwen4Exp-specific DFlash/DFlash 2 training run is different. The target
teacher, long sequences, target feature taps, draft backbone, optimizer state,
and activation memory can exceed the convenient envelope of two 24 GiB cards.
Gradient checkpointing, CPU offload, quantized/frozen teacher execution, and
precomputed features may make it possible, but the runtime and time cost must
be measured. Do not infer it from the sidecar experiment.
## How long will collection take?
The only honest estimate comes from measured replay throughput:
```text
collection_seconds = replay_tokens / sustained_teacher_prefill_tokens_per_second
```
Illustrative—not measured—examples for ten million tokens:
| Sustained teacher replay | Collection time |
|---:|---:|
| 100 tok/s | 27.8 h |
| 250 tok/s | 11.1 h |
| 500 tok/s | 5.6 h |
| 1,000 tok/s | 2.8 h |
Add serialization, checkpoint loading, candidate-path generation, and failed
shards. Candidate-path replay is more expensive than clean-path replay because
it evaluates speculative prefixes that are not present in the accepted thread.
Training time for a small sidecar should be estimated separately:
```text
training_seconds = examples * epochs / sustained_examples_per_second
```
The first milestone should use enough data to falsify the idea, not the largest
dataset that can be collected. A sensible progression is:
```text
100k tokens -> schema, parity, and overfit checks
1M tokens -> first held-out route and calibration curves
5–10M tokens -> workload coverage only if the smaller run improves timely recall
```
## Qualification matrix
### Gate 1 — replay correctness
- exact token IDs and positions;
- exact top-10 ID and rank parity between replay and decode;
- accepted-prefix digest parity;
- no cross-owner or cross-generation opaque-ID reuse;
- deterministic shards under repeated collection;
- fail closed on partial records, unknown versions, digest mismatches, and
counter regression.
If this gate fails, do not train.
### Gate 2 — data integrity and leakage
- split by thread before windowing;
- deduplicate near-identical windows;
- verify train/validation artifact and tokenizer identity;
- quantify domains, languages, prompt lengths, tool calls, and reasoning modes;
- enforce authorization and deletion policy.
### Gate 3 — offline predictor value
Compare at equal candidate count and equal byte budget:
1. random;
2. frequency/LRU control;
3. previous-token same-layer route;
4. adjacent-layer route;
5. target gate applied to an earlier representation, when valid;
6. learned pre-attention predictor;
7. learned DFlash route sidecar.
Report exact-match top-10, recall at candidate budgets, precision, reciprocal
rank, calibration, route-union growth, and stability by layer and domain.
### Gate 4 — decode distribution check
Evaluate the frozen checkpoint on real decode captures that were not regenerated
by replay. Measure the gap between:
```text
replay validation recall
real decode recall
real decode timely recall
```
The third number is decisive. A correct expert arriving after its consumption
deadline is a miss from the scheduler's perspective.
### Gate 5 — shadow I/O
Run predictions and simulate or issue discardable low-priority tickets without
changing target execution. Record:
- predicted physical bytes;
- useful bytes;
- rejected-suffix bytes;
- exact misses avoided;
- cache evictions caused;
- P0/P1 starvation attempts;
- deadline slack;
- exposed exact wait.
### Gate 6 — end-to-end promotion
Promote only if the sidecar reduces output-token latency or increases throughput
under a declared service objective while preserving exact target output and
bounded p95/p99 regression.
## Metrics that prevent self-deception
Route accuracy alone is insufficient. Report:
```text
timely recall
= exact selected experts ready before deadline / exact selected experts
useful prefetch ratio
= predicted physical bytes consumed / predicted physical bytes read
byte amplification
= total physical expert bytes / accepted output tokens
hidden fraction
= 1 - exposed critical I/O wait / total exact I/O wall time
```
For a speculative block of size `B`, let `U_B` be the average unique experts
per layer and `tau(B)` the expected accepted tokens. Relative to top-10
autoregressive routing, the ideal routed-byte break-even remains:
```text
U_B < 10 * tau(B)
```
A higher acceptance length does not guarantee a lower byte cost. A route
predictor can also improve recall while making the system slower through SSD
contention and cache pollution.
## What would falsify the whole approach?
Stop or redesign if any of these remain true after a small controlled run:
1. prefill replay and real decode do not produce exact route-label parity;
2. a previous-token or adjacent-layer heuristic matches the learned predictor
at the same byte budget;
3. DFlash features predict tokens but not target routes;
4. useful recall requires reading so many experts that byte amplification rises;
5. predictions arrive too late to hide a meaningful portion of SSD wait;
6. candidate-path replay costs more than the expected deployment savings can
justify;
7. real agent traffic drifts too quickly for checkpointed predictors;
8. privacy constraints make production transcript replay unacceptable;
9. the exact cold-byte roofline dominates even with perfect prediction;
10. two 3090s spend more time waiting for feature shards than training.
These are successful experimental outcomes if discovered early. The purpose of
the flywheel is to answer whether the workload is predictable enough, not to
guarantee that it is.
## Minimal implementation order
1. Freeze the runtime observation schema and model/tokenizer identity.
2. Capture a small authorized set of real decode traces.
3. Build clean-path replay and prove exact top-10 parity.
4. Produce compact route-label shards without persisting all hidden tensors.
5. Train the smallest shared low-rank predictor on one 3090.
6. Compare it with history and native-gate controls.
7. Add DFlash 2 candidate-path replay only if the clean predictor establishes
that routes are learnable.
8. Train the route sidecar and acceptance-aware calibration.
9. Run shadow P2 scheduling on the M5 Pro.
10. Measure end-to-end SSD wait before considering full DFlash training.
## Final decision
Replaying recorded conversations through prefill is a sound way to turn sparse,
sequential agent traffic into a dense supervised dataset. It is not a shortcut
that makes prefill and decode operationally identical, and it is not novel by
itself.
The research contribution is the contract around it:
```text
decode-shaped examples
prefill-efficient teacher execution
native-router labels
candidate-path lineage
acceptance-aware utility
deadline-aware evaluation
discardable I/O-only predictions
real decode and SSD qualification
```
That boundary makes the proposal both useful and falsifiable. The model may
learn continuously from new snapshots, but the runtime remains deterministic:
the predictor moves bytes early; the native router still makes every decision.
---
# AFN-020 — Port the DFlash2 protocol, not its checkpoint
Project: Hebrus — https://github.com/andreaborio/hebrus
Contribute: https://github.com/andreaborio/hebrus/blob/main/CONTRIBUTING.md
## Retrieval capsule
- Status: verified structural decision
- Phase: DFlash2 compatibility boundary
- Estimated context: capsule ≈487 tokens; full note ≈2020
- Retrieve when: Retrieve this note when evaluating whether a DFlash2 checkpoint can accelerate a different target model, especially when the model names look related or both implementations expose a DFlash2DraftModel.
- Retrieval triggers: reuse DFlash2 weights on another model; DFlash2 for Qwen4Exp; GLM DFlash2 checkpoint compatibility; port speculative decoding to a new target; candidate path accepted prefix replay; target hidden capture contract
- Decision: Treat a released DFlash2 checkpoint as target-specific weights, while porting only its explicit proposal, candidate-path, verification, rollback, and dataset contracts to a newly trained target-specific adapter.
- Invariant: The target model remains the sole authority for token probabilities, acceptance, committed cache state, and native MoE routing; a draft or route sidecar may propose work but cannot redefine target semantics.
- Failure signature: A foreign checkpoint loads because tensor names or architecture labels happen to match, yet hidden widths, target-layer captures, vocabulary rows, selector projections, or cache rollback semantics differ; output may remain plausible while acceptance and routing evidence are invalid.
- Minimal safe implementation: Pin both target and draft revisions, fail closed on every target-bound dimension, define a versioned hidden-capture and verifier trace, replay proposals against the native target, train new draft or sidecar weights, and promote only after greedy/sampling correctness plus target-hardware measurements.
- Rejected shortcut: Rename a GLM or dense-Qwen DFlash2 checkpoint, reshape or truncate incompatible tensors, assume a shared model_type means shared weights, or let predicted routes replace the native top-k router.
- Evidence: Pinned public DFlash2 draft configurations for Qwen3.8-27B and GLM-5.3-Flash, the public z-lab/dflash implementation, and the public Qwen3.8-Flash-Next target configuration
- Evidence checkpoint: incoai/Qwen3.8-27B-DFlash2 dedf8df68adfb1afeaf7b7480c0a0243108177b4; incoai/GLM-5.3-Flash-DFlash2 bf582e4eacc1810f76656d1811693ff6c6737d2a; z-lab/dflash 07ebd93db9f472af339b644bb70221ad8428328a
- Claim boundary: This decision establishes a compatibility boundary and a reusable integration protocol. It does not establish trained Qwen4Exp draft weights, acceptance length, quality parity, SSD savings, Metal performance, or end-to-end Hebrus support.
- Prerequisites: AFN-010, AFN-019
- Related notes: AFN-002, AFN-015
- Supersedes: none
- Canonical URL: https://andreabor.io/agents/port-dflash2-protocol-not-checkpoint/
- Markdown URL: https://andreabor.io/agents/port-dflash2-protocol-not-checkpoint/index.md
## Decision
A DFlash2 release contains two very different things:
1. a useful speculative-decoding protocol;
2. weights trained for one exact target contract.
The first can travel. The second usually cannot.
That distinction matters because related model names create false confidence.
Qwen3.8-27B and Qwen3.8-Flash-Next share a vendor and generation. The public
GLM-5.3-Flash draft even declares the same `DFlash2DraftModel` architecture and
`qwen3` model type as the Qwen draft. None of those labels makes their tensors
interchangeable.
The safe port therefore starts by preserving the protocol and rejecting the
checkpoint.
## The public configs prove target binding
The released
[`Qwen3.8-27B-DFlash2`](https://huggingface.co/incoai/Qwen3.8-27B-DFlash2/blob/dedf8df68adfb1afeaf7b7480c0a0243108177b4/config.json)
and
[`GLM-5.3-Flash-DFlash2`](https://huggingface.co/incoai/GLM-5.3-Flash-DFlash2/blob/bf582e4eacc1810f76656d1811693ff6c6737d2a/config.json)
drafts are both five-layer DFlash2 models, but their target-facing geometry is
already different:
| Public draft | Draft hidden width | Target layers | Vocabulary rows |
|---|---:|---:|---:|
| Qwen3.8-27B-DFlash2 | 5,120 | 64 | 248,320 |
| GLM-5.3-Flash-DFlash2 | 4,096 | 45 | 154,880 |
Qwen3.8-Flash-Next is a third contract. Its public
[`config.json`](https://huggingface.co/Qwen/Qwen3.8-Flash-Next/blob/de4b8e4d43b917e7706784d8bb445c9af86a3540/config.json)
describes a 48-layer sparse-MoE text model with hidden width 2,560, 512 routed
experts per sparse layer, and top-10 routing.
The mismatch is not limited to one input projection. A trained draft may bind
to:
- the target vocabulary and tokenizer identity;
- the number and identity of captured target layers;
- the width and normalization of captured hidden features;
- the draft embedding and output head;
- candidate-selector projections;
- dynamic-convolution weights;
- positional and cache conventions;
- the distribution produced by the exact target revision.
Loading only the tensors whose shapes happen to match is worse than a clean
failure. It creates a partially initialized model whose proposals may look
linguistic while no longer representing the released training contract.
## What is portable
The public
[`z-lab/dflash` implementation](https://github.com/z-lab/dflash/tree/07ebd93db9f472af339b644bb70221ad8428328a)
separates several useful operations. Those operations form the portable part of
the design:
```text
target hidden capture
-> one-pass block proposal
-> top candidates at each position
-> coherent candidate-path selection
-> target verification
-> accepted-prefix commit plus bonus token
-> rollback of rejected suffix state
```
The data shapes, owners, and arithmetic inside each arrow must be rebuilt for
the new target. The existence and order of the arrows are reusable.
For Qwen4Exp, one additional output can be attached to the draft without
changing target semantics:
```text
draft hidden[position]
+-> token candidates
+-> route sidecar -> predicted expert IDs[layer, position]
```
Those expert IDs are prefetch hints only. The target's native router still
computes the exact top-10 set. This is the same authority boundary defined for
SSD scheduling in AFN-019: prediction may move bytes earlier, but it may not
choose executed experts.
## The minimum interoperability contract
A new target adapter should not pass raw framework objects between the runtime,
trainer, and replay tools. Freeze a small versioned record instead.
At minimum, each captured proposal step needs:
```text
schema_version
target_model_revision
tokenizer_revision
request_owner
request_generation
sequence_position
captured_stage
captured_layer_ids
hidden_dtype
hidden_shape
hidden_payload_or_digest
candidate_token_ids
candidate_probabilities_or_logits
selected_candidate_path
target_token_probabilities_or_digest
accepted_prefix_length
bonus_token
```
For an MoE route sidecar, add:
```text
target_route_layer
target_top_k
target_expert_ids_in_native_rank_order
predicted_expert_ids
prediction_confidence
prefetch_ticket_owner
prefetch_ticket_generation
```
The schema should treat the hidden feature as opaque. Consumers can validate
its declared stage, layer IDs, dtype, shape, and target revision without
assuming that a future runtime exposes the same in-memory tensor type.
Owner and generation fields are not bookkeeping decoration. A speculative
request may finish after its suffix has been rejected and its destination slot
reused. A late completion may populate an ordinary cache entry, but it must not
publish into the new request's semantic state.
## Golden replay before training
The first executable milestone does not need a trained draft. It needs a replay
that proves ownership and acceptance semantics.
Use deterministic fixtures to exercise:
1. a proposal containing several candidates at every position;
2. one selected coherent path;
3. target verification of the whole block;
4. zero, partial, and full-prefix acceptance;
5. the correct bonus token;
6. rollback of every rejected cache and hidden-state suffix;
7. cancellation of speculative expert tickets;
8. rejection of a trace with a different target revision, vocabulary, hidden
shape, layer capture, owner, or generation.
This is intentionally model-free. It does not predict eventual acceptance. It
proves that later training cannot silently change the target's authority.
## Train against the target you will verify with
Once the trace contract is frozen, generate labels from the exact target
revision that will run in production.
For token drafting, the teacher data includes target hidden features and target
token distributions at the declared capture points. For expert prefetch, the
same target run can emit native top-10 expert IDs for selected future layers and
positions. A route head can then be trained as a ranking sidecar without
modifying the frozen router.
The promotion ladder is:
```text
schema validation
-> golden proposal/replay
-> target-specific training
-> held-out acceptance and calibration
-> greedy equality
-> sampling distribution checks
-> bytes per accepted token
-> exposed SSD wait
-> end-to-end target-hardware latency
```
Acceptance length alone is not enough for an SSD-heavy MoE. A larger speculative
block may increase rejected expert reads, evictions, and verifier work. Measure
accepted tokens together with exact bytes, speculative bytes, route timely
recall, amplification, and exposed storage wait.
The public Qwen3.8-27B model card reports strong H200 results for its declared
target and setup. Those numbers are evidence for that pair. They are not a
prior for Qwen4Exp on Apple Silicon.
## Compatibility gate
Before allowing a draft checkpoint to load, compare at least:
- exact target and tokenizer revisions;
- vocabulary identity and special-token mapping;
- target layer count and captured layer IDs;
- every hidden, embedding, selector, convolution, and output dimension;
- positional encoding and attention configuration;
- candidate count, block-size limits, and probability representation;
- verifier mode for greedy and sampling;
- cache ownership, rollback, and accepted-prefix semantics.
Any mismatch should reject the checkpoint. An explicit converter is acceptable
only when it describes a mathematically justified transformation and has its own
golden parity evidence. Slicing, padding, repeating, or renaming tensors is not
a converter.
## Recovery rule
When another DFlash2 checkpoint appears, ask two questions in order:
1. **Can these weights satisfy the exact target contract?** Usually only for the
target and revision named by the release.
2. **Which protocol surfaces can we reuse?** Proposal shape, candidate path,
verifier, accepted-prefix replay, rollback, trace schema, and metrics are the
productive starting points.
This avoids both extremes: discarding a useful design because its weights do
not fit, and forcing foreign weights into a runtime because both repositories
contain the string `DFlash2`.
---
# AFN-019 — Hide SSD misses before the router knows they exist
Project: Hebrus — https://github.com/andreaborio/hebrus
Contribute: https://github.com/andreaborio/hebrus/blob/main/CONTRIBUTING.md
## Retrieval capsule
- Status: hypothesis
- Phase: 8–11 performance architecture
- Estimated context: capsule ≈546 tokens; full note ≈5556
- Retrieve when: Retrieve this note when implementing Qwen4Exp expert or PLE SSD streaming, combining ExpertMajor v2 with DFlash, designing route prediction, or deciding whether I/O latency can be overlapped with Metal compute.
- Retrieval triggers: hide SSD expert latency behind compute; DFlash with ExpertMajor v2; predict MoE routes before the native router; PLE and expert I/O priority; adaptive speculative block size for an offloaded MoE; Metal I/O wavefront scheduler
- Decision: Treat SSD-heavy Qwen4Exp inference as a deadline-scheduled wavefront and test three lookahead tiers—exact PLE requests, pre-attention expert prediction, and DFlash route prediction—while keeping the native router and transactional target state authoritative.
- Invariant: Prediction may change which bytes arrive early, but it must never change native top-10 routing, target arithmetic, token acceptance, committed sequence state, or the priority of exact critical reads.
- Failure signature: The runtime reports cache hits or high SSD throughput while token latency still contains layer-by-layer wait bubbles, speculative expert reads starve PLE or exact misses, rejected DFlash suffixes amplify bytes per accepted token, or a predictor silently becomes the execution router.
- Minimal safe implementation: Use generation-owned I/O tickets with deadlines and confidence, separate exact and speculative queues, reconcile predictions against the native router, stream ExpertMajor components through bounded buffers, publish only accepted target state, and measure timely recall, useful bytes, amplification, and exposed wait rather than hit rate alone.
- Rejected shortcut: Prefetch every plausible expert, fix DFlash at its largest block, wait for all selected records before starting compute, use one global I/O queue, or treat Metal I/O capability and paper speedups as evidence that the M5 path is profitable.
- Evidence: Public Qwen4Exp geometry, established Hebrus storage and transaction contracts, published expert-prefetch research, DFlash 2 design evidence, and documented Metal I/O capabilities; no Qwen4Exp M5 performance checkpoint yet
- Evidence checkpoint: Hebrus c3759b5 plus Qwen/Qwen3.8-Flash-Next revision de4b8e4d43b917e7706784d8bb445c9af86a3540; hypothesis only
- Claim boundary: This note does not establish expert-predictor accuracy, an M5 SSD bandwidth figure, a winning DFlash block size, an ExpertMajor codec, end-to-end acceleration, quality parity, or Qwen4Exp runtime support. Every mechanism remains gated by physical traces and exact target-state tests.
- Prerequisites: AFN-002, AFN-003, AFN-010
- Related notes: AFN-015, AFN-016
- Supersedes: none
- Canonical URL: https://andreabor.io/agents/hide-ssd-misses-before-router/
- Markdown URL: https://andreabor.io/agents/hide-ssd-misses-before-router/index.md
## Decision
An SSD-heavy Qwen4Exp runtime cannot be organized as:
```text
router -> discover misses -> read every record -> wait -> execute experts
```
That sequence places storage on the critical path of every layer. The working
hypothesis is to turn inference into a deadline-scheduled wavefront in which
known and predicted data movement begins at the earliest semantically valid
point, compute starts as soon as a useful subset is ready, and another request
or stage fills unavoidable bubbles.
Three kinds of lookahead must remain distinct:
1. **Exact lookahead:** token IDs determine PLE rows before the target reaches
the PLE layer.
2. **Near-term predictive lookahead:** a lightweight pre-attention predictor
estimates the current layer's eventual expert set while GDN or QSA still has
useful work to execute.
3. **Long-range speculative lookahead:** a DFlash drafter may predict expert
sets for several future tokens and target layers before verification starts.
Only the native Qwen4Exp router selects executed experts. Only the verified
target path commits state. Prediction is an I/O hint, never model semantics.
## Why overlap alone cannot defeat the byte roofline
The pinned Qwen4Exp text model has 48 sparse-MoE layers, 512 routed experts per
layer, top-10 routing, hidden width 2,560, and routed intermediate width 640.
Those fields are public in the
[`Qwen3.8-Flash-Next` configuration](https://huggingface.co/Qwen/Qwen3.8-Flash-Next/blob/de4b8e4d43b917e7706784d8bb445c9af86a3540/config.json).
One routed expert contains:
```text
gate + up + down
= 2560*640 + 2560*640 + 640*2560
= 4,915,200 parameters
```
One token therefore activates:
```text
48 layers * 10 experts * 4,915,200
= 2,359,296,000 parameter uses
```
At ideal payload only, that is 562.5 MiB at two bits or 1.0986 GiB at four bits
per token when every selected layer-expert record is cold. Metadata, alignment,
staging, and read amplification are additional. AFN-002 establishes why the
existing ExpertMajor v2 container can describe this family without claiming a
qualified codec.
The lower bound is therefore:
```text
token time >= max(compute time, exact SSD bytes / sustained SSD bandwidth)
+ dependency bubbles that could not be hidden
```
Scheduling can remove the last term and overlap the first two. It cannot make
the storage term disappear. If a measured path needs 600 MiB of exact bytes per
output token, no cache-hit label or queue-depth chart can substitute for the
corresponding bandwidth.
This leads to the first promotion rule:
> Optimize exact bytes per accepted token before optimizing raw I/O throughput.
Useful mechanisms include low-bit storage, sequence-local cache retention,
deduplication of repeated routes across a verifier block, grouping token rows
by expert, and avoiding work on a rejected speculative suffix. Latency hiding
comes after those byte owners are visible.
## Two storage workloads, not one
Qwen4Exp exposes two unrelated access patterns.
### PLE: small, exact, latency-sensitive
PLE requests 16 logical rows per token. The row IDs are derived from known token
history, so a request becomes exact as soon as the token block is chosen. The
physical payload is small, but unrelated rows may occupy unrelated pages. This
makes PLE primarily an IOPS and tail-latency problem.
AFN-003 defines the fixed-page store and the separation between logical row
identity, physical page ownership, checksum, and future codec. The proposed
scheduler preserves that boundary:
```text
DFlash path selection or known autoregressive token
-> exact PLE row IDs
-> page locate, deduplicate, sort, and coalesce
-> high-priority I/O ticket
-> overlap with embedding and target layer 0
-> dependency-scoped wait immediately before PLE use
```
An expert bulk wave must never starve this queue. A PLE ticket for a rejected
DFlash suffix does not commit token history, although a fully read and verified
page may remain as an ordinary cache entry.
### Routed experts: MiB-scale and bandwidth-sensitive
An expert miss transfers a complete gate/up/down record or named component
subranges. The requests are sparse in decode, but their aggregate payload is
large. They require a bulk queue, deeper concurrency, and a very different
replacement policy from PLE.
The cache score should estimate stall removed per byte:
```text
score(record) = P(use before eviction)
* expected exposed latency avoided
/ resident bytes
```
Plain LRU is only a control. Modern decoder-only MoE routes may be too uniform
for popularity alone to deliver a high hit rate; the
[ProMoE paper](https://arxiv.org/abs/2410.22134) reports this as a specific
challenge and also observes that naive prefetch can hurt prefill when nearly
all experts are used.
## Hypothesis H1: exact PLE latency can mostly disappear
PLE has the cleanest lookahead because the address calculation is deterministic.
For DFlash, planning begins immediately after the final candidate path is known.
For ordinary decode, it begins when the current input token is known.
The target then provides a useful cover window:
```text
PLE I/O |==================|
target | embedding | layer 0 GR/GDN/router/MoE | PLE dependency
```
The queue should expose:
- request and unique rows;
- request and unique pages;
- cache hits, joined in-flight reads, and misses;
- logical bytes and physical bytes;
- issue-to-ready latency and ready-to-use slack;
- exposed wait at the PLE boundary;
- rejected-suffix bytes and later cache reuse.
**Falsification:** H1 fails if cold PLE wait remains material after exact early
issue, or if protecting PLE latency measurably degrades expert throughput more
than it saves in target stalls.
## Hypothesis H2: predict expert routes before attention, not after it
The native router sees the representation after the layer's attention/GDN path.
Waiting for it serializes selection, transfer, and expert execution. A separate
predictor can observe the representation available before attention and issue
prefetch hints while the real layer proceeds.
```text
wide residual
|
+-> small pre-attention predictor -> predicted top experts -> SSD tickets
|
+-> native GDN or QSA -> native router -> exact top-10
-> reconcile predictions
```
The predictor must not replace or modify the native router. Prediction errors
affect transfer efficiency only. The
[SpecPrefetch paper](https://arxiv.org/abs/2607.24787) uses the same separation
between prefetch prediction and frozen execution routing. A
[preliminary pre-attention expert-prediction study](https://arxiv.org/abs/2511.10676)
reports that two lightweight projections can predict expert rankings with high
accuracy on several MoE models, including Qwen3-30B, but those figures are not
evidence for Qwen4Exp or Apple Silicon.
The implementation ladder should be deliberately cheap:
1. trace the overlap between the previous token's same-layer top-10 and the
current exact top-10;
2. evaluate the existing native router on an earlier representation as a
zero-training control;
3. if needed, train a low-rank correction or a shared low-rank predictor with
small per-layer heads;
4. prefetch only candidates above a calibrated confidence/deadline threshold.
The predictor emits IDs into a tiny shared buffer. The runtime starts the normal
GDN/QSA command stream without a global wait; a completion handler or small
event transfers only the IDs needed to create file-offset tickets.
The primary metric is **timely recall**:
```text
timely recall = exact selected experts ready before their deadline
/ exact selected experts
```
Report it with precision, bytes prefetched, bytes used, eviction cost, and
exposed exact-miss wait. A predictor that reaches high recall by reading 30
experts for a top-10 router is not successful.
**Falsification:** H2 fails if predictor compute and queue boundaries consume
the lookahead window, if useful timely recall is low under a bounded overfetch
ratio, or if the extra reads increase end-to-end latency despite fewer reactive
misses.
## Hypothesis H3: DFlash can predict routes as well as tokens
DFlash conditions a lightweight parallel drafter on hidden features from the
target and proposes a whole token block in one pass. DFlash 2 adds a top-candidate
path selector and local dynamic convolutions; the released
[Qwen3.8-27B DFlash2 model card](https://huggingface.co/incoai/Qwen3.8-27B-DFlash2#evaluation)
reports longer accepted blocks than the model's native MTP under its published
H200 evaluation.
Those weights are not compatible with Qwen4Exp. The reusable idea is to add a
route sidecar when a Qwen4Exp-specific drafter is eventually trained:
```text
DFlash hidden[position]
+-> token LM head and candidate path
+-> shared low-rank route adapter
-> predicted experts[layer, position]
```
The route labels are already observable from frozen target traces. The sidecar
can therefore be trained as an auxiliary ranking task without changing target
routing. Once the final token path is selected, high-confidence route tickets
for later target layers can begin before verification.
This creates three priority classes:
```text
P0 exact critical native-router miss or exact PLE dependency
P1 near-term predicted pre-attention predictor for an approaching layer
P2 speculative DFlash route sidecar, next-token history, cache warming
```
P2 may consume only bandwidth and cache slots not required by P0/P1. When a
prediction is rejected, its ticket loses priority; completed records become
ordinary cache candidates rather than semantic state.
**Falsification:** H3 fails if draft features do not predict target routes with
useful precision, if a route sidecar materially increases draft time, or if
speculative route overfetch erases the accepted-token gain from DFlash.
## Hypothesis H4: ExpertMajor should stream as a wavefront, not a barrier
After exact top-10 routing, the target should not wait for every selected record
before starting any expert work. ExpertMajor v2 already gives each expert a
contiguous, checksummed record with explicit component extents. That record can
be consumed in bounded waves.
Gate and up are used together; down is needed only after the SwiGLU intermediate
exists. One candidate schedule is:
```text
SSD: gate+up E0 | gate+up E1 | down E0 | gate+up E2 | down E1 | ...
GPU: G/U E0 | G/U E1 | down E0 | G/U E2 | ...
```
Reads may complete out of order. Ready experts can execute immediately into
separate result slots, while the final weighted sum follows a deterministic
route-rank order. This preserves a stable reduction order without making one
slow read block nine ready experts.
The physical selector should test wave widths such as 2, 4, and all selected
experts. One expert per dispatch may waste more command overhead than it hides;
waiting for all ten may recreate the original barrier.
Apple's documentation describes
[Metal resource loading](https://developer.apple.com/documentation/metal/resource-loading)
and the
[`MTLIOCommandQueueDescriptor`](https://developer.apple.com/documentation/metal/mtliocommandqueuedescriptor):
I/O queues can load file data into Metal resources, support priority and
concurrent command limits, and synchronize with compute through shared events.
That makes MTLIO a legitimate candidate, not a proven winner. Bounded `pread`
into shared Metal-visible buffers remains the authoritative fallback until a
physical A/B proves otherwise.
**Falsification:** H4 fails if component splitting lowers effective SSD
throughput, command overhead dominates the saved tail, or concurrent SSD/GPU
traffic slows unified-memory compute enough to remove the overlap.
## Hypothesis H5: prefill and decode need different streaming modes
Large prefill tiles can route tokens to most experts in a layer. In that regime,
sparse random reads may cost more than one sequential layer extent. Decode and a
small DFlash verifier block remain sparse and should use selected records.
The runtime therefore needs an explicit crossover:
```text
macro prefill stream the next full or near-full layer extent sequentially
micro prefill read the sorted, coalesced expert union for the tile
decode route-wise predictive cache and exact misses
DFlash verify block union, deduplicated across token rows
```
During macro prefill, a two-layer ring can load layer `l+1` while the GPU
processes layer `l`. During decode, loading all 512 experts of a future layer is
not acceptable.
The crossover must be measured from exact unique-record fraction, coalesced
task count, physical bytes, and sustained bandwidth. It must not be a hardcoded
prompt-length guess.
**Falsification:** H5 fails if the layer-major sequential path cannot overlap
enough compute to justify its overread, or if the selected-record path remains
faster even when the route union approaches the full layer.
## Hypothesis H6: another request is the only general filler for a hard miss
Within one sequence, later-layer routes depend on earlier-layer results. Some
I/O bubble remains even with prediction. The general filler is independent work
from another admitted request:
```text
request A GDN -> route -> SSD wait --------> experts
request B draft -> QSA -> route -> SSD wait
request C GDN -> experts
GPU A B C A
```
The scheduler should maintain runnable stages rather than synchronizing a whole
batch at each layer. An I/O completion makes one request runnable; the GPU takes
the next ready operation. When several requests reach the same layer, their
route records may be merged and grouped by expert.
This is primarily a throughput mechanism. Single-request TPOT can regress if
the scheduler is unfair. Concurrency is also context-dependent: each session
owns GDN, QSA KV/index, PLE history, and transactional state. Long-context
admission may permit only one lane.
For agent workloads, tool and network waits create another low-priority window.
The runtime may retain or repopulate a sequence-local hot set from public model
state and recent route traces while generation is idle. This remains speculative
cache warming because future tool output is unknown.
**Falsification:** H6 fails if two to four lanes exceed the admitted memory
reserve, increase p95 TPOT beyond the declared service goal, or contend for SSD
and unified memory without improving useful GPU occupancy.
## Hypothesis H7: DFlash block size must follow I/O cost
A fixed maximum block is unsafe for an offloaded MoE. After drafting, the runtime
can estimate for each prefix length:
- expected acceptance;
- predicted and already-resident expert union;
- missing ExpertMajor bytes;
- PLE pages;
- deadline slack;
- rejected-suffix amplification risk.
For block size `B`, expected speculative speed is:
```text
speedup(B) = tau(B) * target_one_token_time
/ (draft(B) + verify(B) + exposed_IO(B) + commit(B))
```
If `U_B` is the average number of unique experts per layer in the verifier block,
the ideal two-bit routed payload per accepted output token is proportional to:
```text
48 * U_B * expert_record_bytes / tau(B)
```
Relative to autoregressive top-10 routing, the routed-byte break-even is:
```text
U_B < 10 * tau(B)
```
For example, a block of five with expected `tau=4` must average fewer than 40
unique experts per layer, rather than its worst-case 50, merely to avoid
increasing routed bytes per output token. This is a formula, not a prediction
of Qwen4Exp route locality.
The governor should truncate the already-produced proposal to the best measured
prefix. Shortening a speculative block does not weaken target correctness.
**Falsification:** H7 fails if route/acceptance estimation costs more than the
adaptation saves, or if a simple fixed block performs within noise across all
qualified cache and prompt conditions.
## Required scheduler contract
Every I/O ticket should name at least:
```text
store and immutable digest
generation and owner request
phase, layer, token/block mask
page or expert/component identity
exact vs predicted
confidence and priority
issue time and consumption deadline
compressed bytes and destination slot
lease count and completion sequence
```
The queue discipline is:
1. exact current PLE and exact native-router misses;
2. near-term high-confidence records with a deadline inside the current layer;
3. later-layer or later-token predictions that fit the remaining budget;
4. background cache warming only when no admitted request can use the bandwidth.
One global `waitUntilCompleted` is a failure. Compute waits on the smallest
event that guards the records it is about to consume. Cancellation releases
ownership exactly once. A stale generation may finish reading bytes but may
never publish them into a recycled cache slot.
AFN-010 remains authoritative for semantic state: GDN recurrence, QSA KV/index,
PLE history and convolution state, cache frontiers, residual streams, and logits
publish as one accepted target transaction. I/O cache entries are not sequence
state.
## Measurements that decide the hypothesis
Cache hit rate and SSD peak throughput are insufficient. Record:
### Storage
- exact, predicted, and rejected bytes;
- logical versus physical bytes;
- queue depth, task sizes, and coalescing;
- cold and warm bandwidth by read size;
- issue-to-ready p50/p95/p99;
- deadline slack or lateness;
- useful-prefetch ratio and overfetch amplification.
### Routing
- exact top-10 IDs by layer/token;
- overlap with previous token and within block sizes 2–5;
- unique experts per layer/block;
- predictor recall, precision, timely recall, and confidence calibration;
- cache eviction caused by false predictions.
### Compute and overlap
- GDN/QSA/router/shared/routed stage time;
- GPU idle time attributable to exact I/O;
- gate/up and down wave completion;
- unified-memory slowdown while I/O is active;
- exposed wait, not merely total I/O wall time.
### End to end
- prefill and decode separately;
- autoregressive, native MTP, and DFlash controls;
- block sizes 1, 2, 3, 4, and 5;
- one request and admitted multi-request lanes;
- cold process, cold store, natural warm store, and hot application cache;
- output identity, transaction digests, pressure, working set, and swap delta.
The central metric is:
```text
hidden fraction = 1 - exposed critical I/O wait / total exact I/O wall time
```
It must be interpreted together with exact bytes per output token. A 95% hidden
fraction achieved by doubling speculative reads is not automatically a win.
## Experiment order and promotion gates
### Gate A — physical storage envelope
Benchmark the actual ExpertMajor candidate record sizes and PLE pages on the
target M5 machine. Compare bounded `pread` into shared buffers with MTLIO at
multiple queue depths. Measure simultaneous GPU compute interference. Do not
fit the scheduler to advertised SSD bandwidth.
### Gate B — trace-only locality
Run the exact target and record route IDs without prediction. Calculate
cross-token overlap, block unions, per-layer skew, cache simulations, and the
byte break-even for block sizes 2–5. This gate can reject DFlash route prefetch
before training anything.
### Gate C — deterministic overlap
Implement exact PLE early issue, dependency-scoped waits, and ExpertMajor
component waves. Prove identical target output and committed state under every
partial completion and injected I/O failure.
### Gate D — pre-attention predictor
Start with zero-training controls, then train the smallest predictor justified
by the trace. Promote only if timely recall reduces end-to-end exposed wait under
a bounded byte-amplification and cache-eviction budget.
### Gate E — verifier-only oracle
Before training DFlash, feed recorded exact future tokens to a block verifier.
This gives perfect acceptance and zero draft cost: an upper bound on what any
drafter can recover after target verification, transactional state, expert I/O,
and PLE costs. If this oracle lacks headroom, stop.
### Gate F — DFlash and route sidecar
Train or obtain a target-specific drafter only after the oracle passes. Compare
token-only DFlash against DFlash plus route prediction. The sidecar wins only if
its added draft latency is smaller than the exact wait it removes.
### Gate G — adaptive and multi-request scheduling
Enable block adaptation and ready-request bubble filling independently, then
together. Use A/B/B/A runs and reject changes whose control drift exceeds the
repository performance protocol.
No gate promotes runtime support. Product exposure still requires the normal
artifact, quality, memory, physical-hardware, and long-run stability evidence.
## Restart capsule for a future agent
When resuming this hypothesis:
1. Read AFN-002 for ExpertMajor family geometry, AFN-003 for PLE store ownership,
and AFN-010 for transactional publication.
2. Confirm the current Qwen4Exp artifact/profile and codec; do not reuse the
ideal two-bit numbers as measured bytes.
3. Collect physical M5 storage traces before adding a predictor.
4. Separate PLE latency from expert bandwidth in every report.
5. Measure route union and exact bytes per accepted token before assuming DFlash
is favorable.
6. Keep the native router authoritative and prediction discardable.
7. Prefer timely recall and exposed wait over cache hit rate.
8. Test component-wave execution without changing deterministic accumulation.
9. Use an oracle verifier to establish an upper bound before drafter training.
10. Leave this note labelled `hypothesis` until a public Hebrus checkpoint
closes the relevant physical and semantic gates.
The durable architectural bet is narrow: prediction should move bytes, not
decisions. If the measured route locality and M5 overlap window are sufficient,
this pipeline can turn SSD stalls into background work. If they are not, the
same instrumentation will show that the remaining problem is unavoidable byte
volume rather than insufficient scheduling cleverness.
---
# AFN-016 — A sparse mask is not sparse attention
Project: Hebrus — https://github.com/andreaborio/hebrus
Contribute: https://github.com/andreaborio/hebrus/blob/main/CONTRIBUTING.md
## Retrieval capsule
- Status: verified checkpoint
- Phase: 6 architecture
- Estimated context: capsule ≈413 tokens; full note ≈817
- Retrieve when: You are implementing sparse or selected attention and need to distinguish mathematically sparse visibility from bounded memory traffic and compute.
- Retrieval triggers: top-k mask still runs dense attention; selected keys but quadratic memory; prove physically sparse QSA; compact K/V gather and online softmax
- Decision: Define sparse attention by physical work: keep compact logical IDs, resolve only selected cache slots, gather only selected K/V rows, and run online softmax over the compact row.
- Invariant: For each query, allocation, K/V reads, score work, and softmax work scale with selected width plus candidate-group scoring, never with the full query-by-key rectangle.
- Failure signature: Telemetry reports a small selected set while memory, dispatches, or kernel time still grow like dense attention because the selected IDs only modify a mask around a generic backend.
- Minimal safe implementation: Carry selected logical IDs to one slot-resolution boundary, compact the referenced K/V rows into bounded scratch, and apply an online softmax that visits exactly those rows.
- Rejected shortcut: Building a dense boolean or additive mask from top-k IDs and calling ordinary dense attention while describing the path as sparse.
- Evidence: Pinned Transformers plus independent NumPy capture; host strict and ASan/UBSan lanes; actual-device Metal selection at 65,537, 100,000, and 262,144 visible tokens; compact gather and online attention; zero dense-mask byte accounting; private allocation, non-completion, and clean-completion gates
- Evidence checkpoint: Hebrus c3759b5
- Claim boundary: Hebrus c3759b5 proves the model-free F32 host and Metal sparse-QSA path through the frozen 262,144-token boundary; it does not register a production profile, family dispatch, artifact codec, full-checkpoint logits claim, or runtime support.
- Prerequisites: AFN-011
- Related notes: AFN-010, AFN-017, AFN-018
- Supersedes: none
- Canonical URL: https://andreabor.io/agents/sparse-mask-is-not-sparse-attention/
- Markdown URL: https://andreabor.io/agents/sparse-mask-is-not-sparse-attention/index.md
## Decision
Sparse attention is a physical-work contract, not a property of a mask.
Qwen4Exp's indexer can reduce one query to at most 512 complete four-token
groups plus a raw tail of at most three tokens: 2,051 visible keys. That result
is mathematically sparse. It becomes an implementation win only if the backend
also avoids allocating, reading, scoring, or masking every other key.
The Phase 6 path verified at
[`c3759b5`](https://github.com/andreaborio/hebrus/commit/c3759b5b096afeb44c4db5768dee9a1de23a63a7)
is:
```text
logical history
-> score complete groups in bounded tiles
-> compact selected logical token IDs
-> resolve selected IDs through the shared slot map
-> gather only selected K/V rows
-> online softmax over at most 2,051 rows
```
Only the gather crosses from logical identity into physical cache layout. The
selection stage does not need to know whether the KV cache wrapped, compacted,
or reused a physical slot.
## Why a sparse mask can still be dense
The public llama.cpp QSA integration at
[`c88c916`](https://github.com/ggml-org/llama.cpp/commit/c88c916) is valuable
semantic evidence: it pools complete groups, preserves the raw index-key cache,
compares selected indices directly, and catches the crucial
`sum(ReLU(dot_h))` ordering. Its graph then turns selected IDs into a mask around
the generic attention builder.
That is a valid engineering reference, but a mask alone cannot prove what every
backend physically traverses. The MLX-VLM Qwen4Exp path provides a similar
comparison point: correct selected visibility can still be represented as a
dense query-by-key boolean array before ordinary attention.
Hebrus therefore treats those paths as numeric and transition references, not
as proof of bounded accelerator work.
## What the checkpoint measures
Selected-count telemetry alone would be insufficient. The checkpoint freezes:
- candidate groups scored;
- selected groups and expanded token width;
- raw-key, K/V, pooled-key, workspace, and total allocation byte ownership;
- scratch sized as groups plus budget plus selected width, never queries by keys;
- compact gather and online-attention outputs against the host oracle;
- actual-device selection at 65,537, 100,000, and 262,144 visible tokens.
The key negative assertion is simple: the dense-mask allocation counter remains
zero.
## Why compact gather comes before fusion
Gathering into bounded contiguous scratch adds one explicit copy, but it makes
ownership and work visible. The selected row has a hard maximum, cache-slot
resolution happens once, and the simple attention kernel becomes directly
comparable to the dense oracle.
A later fused indirect-load kernel may remove scratch traffic. It should replace
the gather only after profiling, without changing the logical-ID contract or
weakening the zero-dense-allocation gate.
## Failure boundary
AFN-011 remains the bounded dense oracle through 2,051 visible keys.
[`c3759b5`](https://github.com/andreaborio/hebrus/commit/c3759b5b096afeb44c4db5768dee9a1de23a63a7)
closes the model-free sparse replacement, its byte accounting, long boundaries,
and publication rollback. It is linked but no production profile, family
dispatch, artifact codec, or runtime-support selector can reach it.
---
# AFN-017 — Top-k chooses the set; time chooses the order
Project: Hebrus — https://github.com/andreaborio/hebrus
Contribute: https://github.com/andreaborio/hebrus/blob/main/CONTRIBUTING.md
## Retrieval capsule
- Status: verified checkpoint
- Phase: 6 architecture
- Estimated context: capsule ≈412 tokens; full note ≈705
- Retrieve when: You have a top-k selector whose mathematical result is a set, but downstream cache gathers and floating reductions require one canonical sequence.
- Retrieval triggers: top-k membership versus output order; sparse row differs below budget; heap order changes floating attention; deterministic selected ID sequence
- Decision: Use deterministic score order only to choose the top-k set, then emit the chosen groups in ascending logical position before expansion and tail append.
- Invariant: Selection membership depends only on score and the frozen tie rule; the emitted token row is always chronological and is byte-identical to dense visibility whenever every complete group fits the budget.
- Failure signature: The correct groups are selected but appear in rank order, so below-budget sparse rows differ from dense rows and finite-precision attention changes with heap or parallel-reduction details.
- Minimal safe implementation: Select membership with descending score and ascending group-position ties, sort the surviving group IDs by position, expand each group contiguously, then append the raw tail.
- Rejected shortcut: Treating top-k rank order as the cache-read and reduction order merely because exact real-number attention is permutation invariant.
- Evidence: Pinned Transformers and independent NumPy selected-ID capture; exact host full-raw/incremental equality; dense parity at 0–5 and 2,047–2,052 tokens; ascending-position tie tests; chunk/decode equality; actual-device Metal selection through 262,144 visible tokens
- Evidence checkpoint: Hebrus c3759b5
- Claim boundary: Hebrus c3759b5 proves the ordering rule for the model-free host and Metal QSA path, including dense parity, equal-score ties, full-raw/incremental identity, and long boundaries; it does not qualify a production profile or full-checkpoint inference.
- Prerequisites: AFN-011
- Related notes: AFN-016, AFN-018
- Supersedes: none
- Canonical URL: https://andreabor.io/agents/top-k-set-time-order/
- Markdown URL: https://andreabor.io/agents/top-k-set-time-order/index.md
## Decision
Do not confuse a ranked set with an execution sequence.
QSA ranks complete four-token groups by score. Ranking answers one question:
which groups fit inside the 512-group budget? Attention needs a second answer:
in which order should the resulting token row be gathered and reduced?
Hebrus gives those questions different contracts:
```text
candidate scores
-> choose set by (score descending, group position ascending)
-> sort chosen group IDs by logical position
-> expand every group to four consecutive token IDs
-> append the incomplete raw tail
```
## The dense-parity property
Before the history exceeds the group budget, every complete group is selected.
If emission is chronological, the sparse row is then byte-for-byte the ordinary
dense causal row, followed by the same incomplete tail.
That creates a powerful local oracle: below the cut, any selected-ID difference
is a selection or ordering defect before attention arithmetic enters the
comparison.
Rank-order emission throws this property away. The chosen membership may still
be correct, but the row now depends on score distribution and selector internals
even when nothing needed to be dropped.
## Floating point makes order observable
In exact arithmetic, attention over the same key/value set is permutation
invariant. A real online softmax is a sequence of finite-precision maximum,
rescale, exponent, denominator, and accumulator updates. Changing the visit
order can change the final bits.
That does not make chronological order uniquely mathematical. It makes one
canonical order necessary if host, Metal, dense-oracle, and future fused paths
must remain reproducible.
Chronology is also the useful systems order: selected logical IDs are monotonic,
four-token groups stay contiguous, and cache gathers can coalesce nearby rows.
## Tie handling belongs to membership
Equal scores choose the smaller logical group position. This tie rule is applied
while selecting membership, not recovered from the final chronological sort.
Otherwise an over-budget tie could select a different set even though the
emitted rows look well ordered.
Tests must therefore distinguish:
- set membership under equal scores;
- chronological emission of that set;
- group-contiguous expansion;
- raw-tail placement;
- below-budget identity with the dense row.
## Failure boundary
[`c3759b5`](https://github.com/andreaborio/hebrus/commit/c3759b5b096afeb44c4db5768dee9a1de23a63a7)
verifies this separation in the model-free host and Metal paths. The strict
lane covers exact selected IDs, equal-score ties, full-raw/incremental identity,
the dense-parity cut around 2,051 visible tokens, and actual-device selection
through 262,144. It does not make the linked partition production-selectable or
qualify full-checkpoint inference.
---
# AFN-018 — A hole in cache history is not padding
Project: Hebrus — https://github.com/andreaborio/hebrus
Contribute: https://github.com/andreaborio/hebrus/blob/main/CONTRIBUTING.md
## Retrieval capsule
- Status: verified checkpoint
- Phase: 6 architecture
- Estimated context: capsule ≈419 tokens; full note ≈691
- Retrieve when: You are grouping cached tokens by logical position and need to distinguish legal left padding or eviction from an internal gap that would silently drop attention history.
- Retrieval triggers: hole inside KV cache history; left padding versus missing token; group adjacent physical cache cells; sparse attention silently drops history
- Decision: Model each visible sequence as one contiguous logical-position run: permit an arbitrary first position for left padding, require every append to advance by one, and reject internal holes before sparse planning.
- Invariant: Every live position in a visible sequence run has exactly one predecessor except the first; complete groups and the raw tail partition that run without gaps or overlap.
- Failure signature: A removed or stale middle token leaves earlier live tokens that belong to neither a complete group nor the final tail, so sparse attention silently forgets valid history.
- Minimal safe implementation: Anchor grouping at the first live logical position, enforce successor appends, validate contiguity before planning, and rebuild pooled groups transactionally after lifecycle operations.
- Rejected shortcut: Treating a missing middle slot as ordinary padding, grouping adjacent physical cells, or skipping the gap and continuing group numbering.
- Evidence: Host strict and ASan/UBSan tests for left-padding anchors, internal-hole rejection, slot reuse, wrap eviction, reset, copy, fork, rewind, shift, remove, serialization and atomic restore; actual-device Metal rejection of groups crossing a position hole
- Evidence checkpoint: Hebrus c3759b5
- Claim boundary: Hebrus c3759b5 verifies the contiguous-run and transactional lifecycle contract in the model-free QSA cache and Metal selection path; it does not expose long QSA state through a production profile, artifact codec, or runtime-support admission.
- Prerequisites: AFN-010, AFN-011
- Related notes: AFN-016, AFN-017
- Supersedes: none
- Canonical URL: https://andreabor.io/agents/cache-hole-is-not-padding/
- Markdown URL: https://andreabor.io/agents/cache-hole-is-not-padding/index.md
## Decision
Padding is outside a sequence. A hole is inside it. Sparse grouping cannot treat
them as the same thing.
For one logical sequence, the first real token may start at any absolute
position. That is enough to represent left padding or an older prefix that has
already been evicted. After that anchor, every visible token must be the exact
successor of the previous one.
```text
legal left padding: [100, 101, 102, 103, 104]
legal shifted history: [4096, 4097, 4098, 4099]
illegal internal hole: [100, 101, 103, 104]
```
## Why QSA cannot skip the gap
QSA partitions the visible run into complete four-token groups plus one final
raw tail of zero to three tokens. An internal gap breaks that partition.
If position 102 is missing, the implementation cannot safely reinterpret 103 as
the third token of the earlier group: group identity and RoPE position would be
wrong. It also cannot discard 100 and 101 as if they were a tail, because the
tail is defined only at the current frontier.
Continuing after the hole therefore makes some live tokens disappear from
attention without an explicit policy decision.
## Logical identity before physical layout
The public llama.cpp hybrid index cache at
[`ff24f38`](https://github.com/ggml-org/llama.cpp/commit/ff24f38) establishes the
important ownership direction: index state follows the main cache's sequence,
copy, remove, and restore lifecycle. The QSA integration at
[`c88c916`](https://github.com/ggml-org/llama.cpp/commit/c88c916) likewise forms
blocks from positions rather than adjacent cache cells.
Hebrus keeps that semantic boundary and makes the missing-middle case explicit:
physical wrap, compaction, and slot reuse are allowed; a discontinuity in the
logical run is not.
## Reject early, rebuild transactionally
The cheapest time to preserve contiguity is append: the first position is free,
and every later reservation must target `frontier + 1`.
Lifecycle operations still need validation. Remove, rewind, shift, fork, copy,
and restore can change the derived pooled groups. They rebuild those groups in a
private candidate state and publish only when the complete run is valid.
If a caller intentionally needs segmented attention, that is a new semantic
model with explicit segments—not permission to reinterpret a hole.
## Failure boundary
[`c3759b5`](https://github.com/andreaborio/hebrus/commit/c3759b5b096afeb44c4db5768dee9a1de23a63a7)
verifies the fail-closed cache model across left-padding anchors, internal
holes, slot reuse, wrap eviction, rewind, shift, copy/fork, serialization,
atomic restore, and multiple sequences. The implementation remains a linked
model-free partition: no production profile, artifact codec, or runtime-support
admission selects it.
---
# AFN-015 — A phase handoff needs a decision delta
Project: Hebrus — https://github.com/andreaborio/hebrus
Contribute: https://github.com/andreaborio/hebrus/blob/main/CONTRIBUTING.md
## Retrieval capsule
- Status: verified checkpoint
- Phase: agent operations
- Estimated context: capsule ≈410 tokens; full note ≈686
- Retrieve when: You are closing an implementation phase or architecture checkpoint and need to preserve reusable reasoning without publishing progress logs or unverified claims.
- Retrieval triggers: phase handoff missing reusable decisions; agent repeats settled architecture research; publish technical choice after implementation; closeout checklist for autonomous agent
- Decision: Require Andrea-launched agents to perform a decision-delta review before closing each verified phase, publishing reusable evidence-bound choices or explicitly recording that no new note is warranted.
- Invariant: A verified phase handoff always states whether it produced a new reusable decision, and every published decision names public evidence plus the boundary of what that evidence does not prove.
- Failure signature: A later agent repeats a settled investigation, reintroduces a rejected shortcut, or learns an invariant only by reconstructing an old diff because the prior handoff recorded tests but not the reusable decision.
- Minimal safe implementation: At phase close, compare the work against the Field Notes selector, publish one compact note for each genuinely new evidence-backed decision, or record an explicit no-new-decision result in the handoff.
- Rejected shortcut: Publishing a diary of changed files, copying the whole handoff into the archive, or labelling dirty and local-only work as a verified public checkpoint.
- Evidence: Agent Field Notes schema v2 already separates fourteen Phase 0-5 decisions into retrieval capsules with explicit evidence and claim boundaries; the closeout rule makes that preservation step deterministic
- Evidence checkpoint: Agent Field Notes schema v2
- Claim boundary: This operator workflow does not bind external contributors, make incomplete work publishable, or replace repository source, tests, accepted decisions, support contracts, and QA gates.
- Prerequisites: AFN-009
- Related notes: AFN-014
- Supersedes: none
- Canonical URL: https://andreabor.io/agents/phase-handoff-needs-decision-delta/
- Markdown URL: https://andreabor.io/agents/phase-handoff-needs-decision-delta/index.md
## Decision
An implementation phase is not fully handed off when the code and tests are
green. It is handed off when the next agent can also recover the new reasoning
without reconstructing it from the diff.
For agents explicitly launched by Andrea, phase close therefore includes one
mandatory question:
> Did this phase establish a reusable decision that the current Field Notes do
> not already preserve?
If the answer is yes, the agent prepares or publishes an evidence-bound note.
If the answer is no, the handoff says so explicitly. External contributors are
not required to follow this operator-specific closeout rule.
## What counts as a decision delta
A note is warranted when the phase establishes at least one reusable item:
- an invariant whose violation can produce plausible but wrong behavior;
- a failure signature that identifies the defect earlier next time;
- a minimal safe implementation or ownership rule;
- a shortcut that was attractive, tested, and rejected;
- an evidence gate that separates structural correctness from support;
- a claim boundary that prevents a narrow result from becoming a broad claim.
A list of files changed, pass counts, or remaining tasks is not a decision
delta. Those belong in the phase handoff.
## The publication boundary
The review happens at phase close, but publication still follows the evidence.
A dirty worktree, local-only commit, private transcript, or unpublished artifact
cannot anchor a verified public note. The candidate remains pending until a
public checkpoint exists.
This delay is useful. It prevents the archive from borrowing authority from
tests or source that another contributor cannot inspect. It also forces the note
to cite the smallest durable checkpoint instead of a transient session state.
## Closeout procedure
1. Open the compact Field Notes selector and search by the phase's new failure
modes, invariants, and rejected alternatives.
2. Remove duplicates and status-only observations.
3. For each remaining decision, write the recovery capsule first: decision,
invariant, failure signature, minimal safe implementation, rejected shortcut,
evidence, checkpoint, and claim boundary.
4. Publish only after the checkpoint and sources are public and the site gate
passes.
5. If nothing remains, add `Agent Field Notes: no new decision delta` to the
handoff.
The standalone machine-readable operator contract lives at
[`/agents/operator-contract.txt`](/agents/operator-contract.txt).
## Failure boundary
This workflow improves retrieval discipline. It cannot prove that an agent
recognized every important decision, that the decision itself is correct, or
that a published note supersedes the repository's normative documents.
---
# AFN-014 — Do not call a contract vector an upstream capture
Project: Hebrus — https://github.com/andreaborio/hebrus
Contribute: https://github.com/andreaborio/hebrus/blob/main/CONTRIBUTING.md
## Retrieval capsule
- Status: verified checkpoint
- Phase: 5
- Estimated context: capsule ≈366 tokens; full note ≈717
- Retrieve when: You are generating golden vectors from a framework that does not expose every cache image, token choice, tie policy, or synthetic control needed by the native test.
- Retrieval triggers: golden fixture generated by code under test; upstream framework does not expose cache state; per-array oracle provenance; synthetic control mislabeled as upstream
- Decision: Record provenance per fixture array and reserve the upstream-capture label for values actually emitted by pinned upstream code; label synthetic identities and state images as contract controls.
- Invariant: A fixture's provenance never claims that upstream produced an array which the collector constructed from the local contract.
- Failure signature: A generated file mixes upstream outputs and locally invented controls under one trusted label, allowing the implementation to validate its own assumption as external truth.
- Minimal safe implementation: Attach an origin to every array, execute pinned upstream for exposed values, independently recompute their math, and label synthetic token, state, tie, and overflow controls explicitly.
- Rejected shortcut: Calling the entire golden file upstream-backed because some arrays passed through the upstream runtime.
- Evidence: Graph fixtures execute pinned Transformers at 42ca97014c85d71a88ad60d55f08cb9fb4d26e2c, cross-check independent NumPy/scalar C, and label non-upstream arrays as contract-control in per-array provenance
- Evidence checkpoint: Hebrus 0626603
- Claim boundary: These tiny graph vectors prove primitive and transition semantics only; they do not contain checkpoint weights, full-layer intermediates, quantization error, or final model logits.
- Prerequisites: AFN-008
- Related notes: AFN-009, AFN-010
- Supersedes: none
- Canonical URL: https://andreabor.io/agents/label-contract-control-vectors/
- Markdown URL: https://andreabor.io/agents/label-contract-control-vectors/index.md
## Decision
Fixture provenance belongs to each array, not merely to the file.
The Phase 5 collector executes the pinned Transformers Qwen4Exp implementation to capture values that upstream actually computes. An independent NumPy/scalar implementation checks the same math. That provides a strong oracle for exposed module outputs.
Some necessary test inputs and states do not have an upstream capture path. Token sequences are selected by the fixture author. Hashed PLE row identities and persistent cache images may need to be constructed so a specific transition can be tested. Tie, overflow, and rollback controls can also be contractual rather than upstream-authored.
Those arrays are useful—but they are not upstream captures.
## The self-approval trap
Suppose a local implementation defines a cache layout, generates an expected cache image using the same assumption, and stores both beside genuine upstream outputs. If the whole file is labelled “generated from Transformers,” a later agent may treat the cache image as independent evidence.
The implementation has then approved itself while borrowing upstream's authority.
## Provenance classes
A practical fixture can distinguish at least:
- **upstream capture** — emitted by the exact pinned implementation;
- **independent cross-check** — recomputed through a separate algorithm or language;
- **contract control** — deliberately selected input, state image, tie, overflow, or failure case;
- **derived metadata** — dimensions, offsets, or digests computed from already identified sources.
Each class answers a different question. None should silently inherit the authority of another.
## Regeneration rules
The networked collector may be needed to reproduce upstream arrays, but the normal repository test should stay offline. It can pin source revision, tool versions, source hash, per-array provenance, and aggregate fixture digests. Offline validation then detects drift without downloading a framework during every build.
The collector and the native implementation should not share the optimized helper being tested. Agreement is valuable only when likely defects are independent.
## Agent checklist
Before trusting a golden fixture, ask:
1. which exact function produced each expected array;
2. whether that function is upstream, independent, or part of the implementation;
3. which arrays are intentionally synthetic controls;
4. whether regeneration pins immutable source identity;
5. whether ordinary checks detect changes without network access;
6. what the fixture cannot say about full-model behavior.
## Failure boundary
Phase 5's fixture is intentionally tiny and model-free. It closes graph ordering and state-transition questions; it does not replace checkpoint-backed layer captures, final-logit parity, quality evaluation, or performance measurement.
---
# AFN-013 — Correctness code can exist without becoming a product feature
Project: Hebrus — https://github.com/andreaborio/hebrus
Contribute: https://github.com/andreaborio/hebrus/blob/main/CONTRIBUTING.md
## Retrieval capsule
- Status: verified checkpoint
- Phase: 5
- Estimated context: capsule ≈394 tokens; full note ≈660
- Retrieve when: You need realistic integration and hardware tests for a new backend or model family without accidentally advertising, auto-selecting, or accepting it in production.
- Retrieval triggers: test backend accidentally exposed in production; compiled code mistaken for supported feature; staged model-family rollout; fail-closed product registration
- Decision: Link the reviewed correctness implementation behind an unreachable test seam, while keeping product admission fail-closed until artifact, memory, quality, and performance gates qualify an execution route.
- Invariant: Passing private host or accelerator correctness tests cannot make the model selectable through normal loader, server, downloader, or support metadata.
- Failure signature: A test-only kernel is linked, generic dispatch discovers it, and users can reach an unqualified artifact or backend because compiled code was mistaken for a support contract.
- Minimal safe implementation: Separate correctness symbols from product registration, require an absent physical profile or capability selector to reject normal execution, and test both the positive private seam and negative public route.
- Rejected shortcut: Using a hidden command-line flag, undocumented alias, or optimistic architecture match to expose code before the release evidence exists.
- Evidence: The host coordinator and Metal partition compile and pass model-free gates while remaining unselected; normal builds expose no physical profile, server route, downloader entry, or support claim
- Evidence checkpoint: Hebrus 0626603
- Claim boundary: Phase 5 proves tiny model-free host and physical-Metal semantics only; real checkpoint parity, codec choice, SSD streaming, memory fit, throughput, server, downloader, and release support remain disabled.
- Prerequisites: AFN-004, AFN-008
- Related notes: AFN-001, AFN-010, AFN-011
- Supersedes: none
- Canonical URL: https://andreabor.io/agents/correctness-code-can-stay-unreachable/
- Markdown URL: https://andreabor.io/agents/correctness-code-can-stay-unreachable/index.md
## Decision
Compilation is not capability.
Hebrus Phase 5 links a resident Qwen4Exp graph coordinator and a dedicated Metal correctness partition. They can be exercised by focused tests on a real device. Normal execution still has no Qwen4Exp physical profile and therefore fails during admission before graph allocation.
The same separation applies above the runtime: there is no server alias, downloader entry, release artifact, or support-table promotion.
## Why linking the code is useful
Keeping all correctness work in an isolated prototype delays integration defects. The code needs to encounter the real build, symbol boundaries, Metal source assembly, ownership conventions, and regression suite before artifact choices are frozen.
But integration creates a second risk: generic architecture dispatch or fallback matching may discover a half-qualified path. A plausible output is especially dangerous because it can turn a research implementation into an undocumented public API.
## Two independent gates
The safe structure has both:
1. a private positive seam proving that the host coordinator and Metal primitives work;
2. a public negative seam proving that ordinary product selection cannot reach them.
The second test is not redundant. It verifies the absence of capability, not the correctness of implementation.
In Hebrus the missing production physical profile is the fail-closed boundary. Later phases must deliberately add artifact identity, codec bindings, memory policy, quality and performance evidence, and product metadata before that boundary changes.
## Language matters
Status labels should describe the reachable contract:
- **implemented** can mean code exists;
- **correctness fixture** means a bounded test seam passes;
- **pinned-not-supported** means normal users still cannot select it;
- **supported** requires the declared product and hardware gates.
Collapsing these states into “works” makes documentation itself a release bug.
## Agent checklist
When staging a new model family or accelerator path:
1. name the private entry point used by correctness tests;
2. name the public selector that must remain absent or rejecting;
3. test both independently;
4. keep server, downloader, aliases, and support tables unchanged;
5. state exactly which later evidence authorizes promotion;
6. rerun established families whenever shared dispatch changes.
## Failure boundary
The Phase 5 Metal fixture uses deterministic small F32 arrays. It does not load the production checkpoint or prove that a release artifact fits, runs quickly, survives long sessions, or matches expected quality.
---
# AFN-012 — An ubatch is not a sequence boundary
Project: Hebrus — https://github.com/andreaborio/hebrus
Contribute: https://github.com/andreaborio/hebrus/blob/main/CONTRIBUTING.md
## Retrieval capsule
- Status: verified checkpoint
- Phase: 5
- Estimated context: capsule ≈373 tokens; full note ≈655
- Retrieve when: You are chunking a stateful convolution, hash-derived embedding, or recurrent prefill path and need batching changes to preserve exact sequence semantics.
- Retrieval triggers: prefill chunk size changes logits; dilated convolution state across batches; sequence state leaks between batch rows; EOS changes n-gram embedding history
- Decision: Own PLE history and dilated-convolution state by logical sequence, carrying it across ubatches and updating it in the frozen EOS order.
- Invariant: For the same token sequence, PLE row IDs, convolution outputs, persistent state, and final logits are identical across every valid ubatch partition, while separate sequences share no mutable history.
- Failure signature: Changing prefill chunk size changes row hashes or logits, the first token after a boundary sees zeroed history, or one sequence consumes another sequence's convolution tail.
- Minimal safe implementation: Store history and the full dilated receptive-field tail per sequence, advance them token by token inside each ubatch, and parity-test multiple partitions plus EOS and independent-sequence controls.
- Rejected shortcut: Resetting convolution or n-gram history at each scheduler chunk, or indexing persistent state by a transient batch row without generation ownership.
- Evidence: One-shot and 1/2/1-token ubatch plans produce identical PLE rows, convolution state, public digest, and logits; independent-sequence and current-EOS controls pass on host and Metal
- Evidence checkpoint: Hebrus 0626603
- Claim boundary: This proves model-free PLE state transitions with resident synthetic rows; it does not qualify the 320M-row production codec, page cache, SSD reads, or I/O overlap.
- Prerequisites: AFN-003, AFN-008
- Related notes: AFN-010, AFN-013
- Supersedes: none
- Canonical URL: https://andreabor.io/agents/ubatch-is-not-sequence-boundary/
- Markdown URL: https://andreabor.io/agents/ubatch-is-not-sequence-boundary/index.md
## Decision
Scheduler chunks must not become model boundaries.
Qwen4Exp's PLE path combines token-history-derived row selection with a dilated causal convolution. Both operations depend on preceding tokens. A runtime is free to divide prefill into ubatches for memory or scheduling reasons, but that division is not visible to the model.
Hebrus therefore owns PLE history and convolution tails per logical sequence and carries them across every ubatch. The state advances in token order, including the frozen current-EOS rule, regardless of how the caller partitions the same input.
## The hidden receptive field
A causal convolution with kernel width four and dilation three needs nine preceding positions of state. Keeping only the last contiguous three values would look plausible and still be wrong: the next output reads spaced historical positions.
The correct persistent state is derived from the receptive field, not from the number of coefficients or the current ubatch size.
The history hash has a similar trap. Resetting at a chunk boundary changes bigram and trigram row identities even though the user supplied one continuous sequence.
## Evidence shape
Phase 5 compares the same token sequence executed in one shot and in a 1/2/1-token partition. It checks more than final logits:
- the generated PLE row identities;
- persistent convolution bytes;
- the digest of all public state owners;
- final output;
- behavior at the EOS transition;
- isolation between independent sequence state images.
Comparing only the last output could allow offsetting state defects to escape.
## Ownership rule
Persistent sequence state should be addressed by a stable slot plus generation, not by the temporary row number of the current ubatch. Copy, rewind, reset, and future cache reuse must operate on the history and convolution group together.
This is also why the state belongs inside the whole-chunk transaction described in AFN-010. A failed later stage must not leave PLE one token ahead of the public frontier.
## Agent checklist
For any chunked stateful operator:
1. derive the exact historical receptive field;
2. separate logical sequence ownership from batch placement;
3. specify update order around EOS and reset events;
4. compare internal state as well as output across partitions;
5. include a multi-sequence isolation case;
6. make failure preserve the pre-chunk snapshot.
## Failure boundary
These tests use tiny resident rows and deterministic F32 computation. Production PLE storage, decoding, page leases, cancellation, cold/warm behavior, and SSD overlap remain Phase 7 work.
---
# AFN-011 — A reference algorithm needs an expiry condition
Project: Hebrus — https://github.com/andreaborio/hebrus
Contribute: https://github.com/andreaborio/hebrus/blob/main/CONTRIBUTING.md
## Retrieval capsule
- Status: verified checkpoint
- Phase: 5
- Estimated context: capsule ≈342 tokens; full note ≈572
- Retrieve when: You need a simple reference path to unlock graph bring-up without allowing its memory complexity or semantics to leak into advertised long-context support.
- Retrieval triggers: reference attention becomes production path; quadratic long-context allocation; dense versus sparse parity plan; boundary test at first unsupported context
- Decision: Use dense QSA only as a bounded correctness oracle through 2,051 visible keys and reject larger contexts until the native sparse algorithm is independently qualified.
- Invariant: No request above the declared dense-oracle ceiling can allocate or execute a dense Q-by-K QSA path.
- Failure signature: A small-context reference implementation passes tests and is accidentally reused at production context, creating quadratic allocation, hangs, OOM, or a false support claim.
- Minimal safe implementation: Encode the reference ceiling in geometry admission and backend dispatch, test the exact last accepted and first rejected values, and keep sparse support behind a later parity gate.
- Rejected shortcut: Calling the dense implementation temporary while leaving its context limit as documentation, an optional flag, or an unchecked allocation.
- Evidence: Host and physical-Metal boundary tests accept the inclusive 2,051-key dense case and reject 2,052 plus 262,143/262,144/262,145 before graph execution
- Evidence checkpoint: Hebrus 0626603
- Claim boundary: The dense oracle establishes small-context QSA semantics only; it does not implement sparse selection, long-state lifecycle, 100K context, or production memory behavior.
- Prerequisites: AFN-001, AFN-008
- Related notes: AFN-004, AFN-010, AFN-013
- Supersedes: none
- Canonical URL: https://andreabor.io/agents/dense-reference-needs-expiry/
- Markdown URL: https://andreabor.io/agents/dense-reference-needs-expiry/index.md
## Decision
Every deliberately simple oracle needs a machine-enforced expiry condition.
Phase 5 uses dense QSA because it is easy to inspect and compare with pinned reference vectors. That is appropriate for correctness bring-up. It is not a viable implementation for the model's long-context target, where forming dense query-by-key work would defeat the sparse design.
Hebrus therefore accepts the dense path only through 2,051 visible keys. The 2,052 case fails closed, as do explicit cases around the 262K model boundary. Rejection happens before a dense graph or proportional allocation is reachable.
## Why the boundary belongs in code
“Temporary” is not a runtime property. Once reference code compiles, links, and returns plausible results, later agents can mistake it for the supported path—especially after the original implementation conversation is gone.
A prose warning also fails too late. An oversized dense allocation can already create memory pressure before a downstream kernel notices that sparse QSA is unavailable.
The limit must therefore live in geometry validation and dispatch, alongside tests for:
- the exact largest accepted input;
- the first rejected input;
- values near the advertised architectural maximum;
- unchanged output and state on rejection.
## What the dense path is for
The bounded implementation remains valuable. It gives the future sparse path a local oracle for all visible-key cases up to the ceiling. The sparse implementation can be compared against the same selection, cache position, partial-tail, and attention semantics without involving an entire checkpoint.
That is a stronger role than “slow fallback.” It is executable specification with a finite domain.
## Agent checklist
Before merging a reference algorithm, record:
1. the domain in which it is authoritative;
2. the first input it must reject;
3. the resource complexity that makes the boundary necessary;
4. the later implementation that must match it;
5. the gate required before removing or raising the ceiling.
## Failure boundary
Phase 5 does not claim sparse QSA or long-context support. Phase 6 must add tiled sparse selection, compact gathers, cache lifecycle operations, and model-backed long-context evidence before the ceiling can move.
---
# AFN-010 — One bank flip is enough: make the whole inference chunk transactional
Project: Hebrus — https://github.com/andreaborio/hebrus
Contribute: https://github.com/andreaborio/hebrus/blob/main/CONTRIBUTING.md
## Retrieval capsule
- Status: verified checkpoint
- Phase: 5
- Estimated context: capsule ≈374 tokens; full note ≈655
- Retrieve when: You are implementing stateful inference, speculative work, or a multi-stage accelerator graph where one request can fail after mutating recurrent state, KV/index caches, routing state, or logits.
- Retrieval triggers: failed inference corrupts the next request; partial KV or recurrent-state commit; transactional GPU graph publication; chunk and token execution diverge
- Decision: Execute a complete token chunk against a private state bank and publish every persistent owner with one bank-index flip only after all host and Metal stages succeed.
- Invariant: Public frontier, logits, residuals, GDN state, QSA caches, PLE state, and routing state always describe the same successfully completed chunk.
- Failure signature: A failed token leaves the frontier unchanged but advances one cache, convolution row, route, or logits buffer, so the next request starts from a state no valid execution produced.
- Minimal safe implementation: Keep two complete state banks, copy the public snapshot into the private bank, run every fallible stage privately, validate finite values and accelerator status, then publish through one index swap.
- Rejected shortcut: Rolling back only the visibly changed buffer, or copying old bytes back after an accelerator command has partially completed.
- Evidence: Token, chunk, and interleaved multi-turn equivalence; failure injection after every graph stage; non-finite rejection; Metal allocation, command, and status rollback
- Evidence checkpoint: Hebrus 0626603
- Claim boundary: This proves transactional behavior for the Phase 5 model-free resident graph; it does not prove full-checkpoint logits, production codecs, SSD execution, memory fit, or product support.
- Prerequisites: AFN-001, AFN-008
- Related notes: AFN-012, AFN-013, AFN-014
- Supersedes: none
- Canonical URL: https://andreabor.io/agents/whole-chunk-transaction/
- Markdown URL: https://andreabor.io/agents/whole-chunk-transaction/index.md
## Decision
Treat the whole chunk as the transaction—not each kernel, layer, or token-shaped buffer.
In the Phase 5 Hebrus graph, a chunk can update the four-stream residual, GDN convolution and recurrent matrices, QSA K/V and raw-index caches, PLE history and convolution state, selected routes, the public frontier, and logits. These values form one logical state image. Publishing them independently would permit combinations that the model never computed.
## Why local rollback is insufficient
A stage can fail after earlier stages have produced valid private results. The failure may be structural, a non-finite intermediate, an allocation denial, a Metal command that did not complete, or a nonzero device status word. Tracking which individual writes need undo logic turns every future state owner into another rollback branch.
That approach is fragile because ownership grows faster than the rollback checklist. It also asks the host to reconstruct whether device work partially executed.
## The publication model
Hebrus keeps two complete state banks:
1. one bank is the only public state;
2. the other becomes the transaction-private working image;
3. every graph stage mutates only the private image;
4. output remains caller-private while work is fallible;
5. finite checks, command completion, and status words must all pass;
6. one bank-index flip publishes the new frontier, state, and logits together.
Failure therefore discards an unpublished image. It does not attempt to reverse history.
## Evidence that matters
The useful test is not merely “a successful token produces expected logits.” Phase 5 injects failure after every graph stage and verifies two properties: the digest of all public state owners is unchanged, and the caller's output sentinel is untouched. Separate cases cover non-finite arithmetic and Metal allocation, command-completion, and status failures.
The same final state is then required for single-token, differently chunked, and interleaved multi-turn execution. Chunk size is a scheduling choice; it must not change model history.
## Reusable rule
When an inference operation updates more than one persistent owner, define one publication point before adding optimization. If the implementation cannot name that point, it does not yet have transactional state semantics.
## Failure boundary
This milestone uses a tiny deterministic correctness graph. It establishes the transaction seam, not production quality or speed. Real weights, quantized tensors, streamed PLE pages, sparse long-context QSA, and the product execution route remain later gates.
---
# AFN-009 — The recovery packet: enough context to continue, not enough to drown
Project: Hebrus — https://github.com/andreaborio/hebrus
Contribute: https://github.com/andreaborio/hebrus/blob/main/CONTRIBUTING.md
## Retrieval capsule
- Status: verified checkpoint
- Phase: agent operations
- Estimated context: capsule ≈352 tokens; full note ≈1391
- Retrieve when: You need to hand a long-running coding project to a new agent, recover after context compaction, or choose the smallest trustworthy document that can answer one implementation question.
- Retrieval triggers: agent handoff after long implementation; context compaction recovery; project knowledge consumes too many tokens; multiple agents repeat settled research
- Decision: Publish architectural knowledge in selector, recovery-capsule, full-note, and source-evidence tiers instead of handing every agent the whole project transcript.
- Invariant: The smallest retrieval tier must preserve the decision, invariant, failure signature, minimal safe path, rejected shortcut, evidence, and claim boundary.
- Failure signature: A new agent rereads the repository or conversation, repeats settled research, reopens rejected decisions, or mistakes an old status summary for current authority.
- Minimal safe implementation: Maintain a compact selector, a self-contained recovery capsule per decision, an optional full rationale, explicit note dependencies, and links to immutable evidence.
- Rejected shortcut: One ever-growing project summary, a raw transcript dump, or a vector search result without status, provenance, and supersession boundaries.
- Evidence: Applied schema-v2 capsules, graph edges, context estimates, build validation, and machine-readable indexes across AFN-001 through AFN-009
- Evidence checkpoint: Agent Field Notes schema v2
- Claim boundary: This structure reduces retrieval cost and repeated work; it cannot guarantee that an agent selects the right note or that the underlying technical decision is correct.
- Prerequisites: none
- Related notes: AFN-001, AFN-008
- Supersedes: none
- Canonical URL: https://andreabor.io/agents/recovery-packets-for-autonomous-agents/
- Markdown URL: https://andreabor.io/agents/recovery-packets-for-autonomous-agents/index.md
Long-running coding work accumulates two kinds of context:
1. facts needed to continue safely;
2. history explaining how the team discovered those facts.
They are not equally valuable at retrieval time.
A new agent usually needs the exact checkpoint, the decisions that must not drift, the files or state it may own, the next exit gate, and a few known failure modes. Giving it the entire conversation or a large project summary forces the model to rediscover that small working set inside thousands of irrelevant tokens.
The recovery packet is a layered alternative.
## Four retrieval tiers
The Agent Field Notes corpus now exposes four deliberate costs:
```text
selector find the relevant note from triggers and status
capsule recover the operational decision in roughly 150–300 tokens
full note load rationale, alternatives, formulas, and examples
source inspect code, tests, commits, or primary upstream evidence
```
An agent begins at the cheapest tier. It moves downward only when the current tier cannot answer the question.
`llms.txt` is a selector, not a compressed encyclopedia. It provides retrieval triggers, failure signatures, status, dependencies, links, and approximate context cost. The individual Markdown page begins with the recovery capsule. `llms-full.txt` exists for deliberate corpus ingestion, not as the default prompt attachment.
## What belongs in the capsule
A capsule is not an abstract. An abstract describes the document; a recovery capsule restores the decision state.
It contains:
- **Retrieve when** — the task situation that makes the note relevant.
- **Decision** — the selected architectural action.
- **Invariant** — the property future work may not violate.
- **Failure signature** — what the mistake looks like in practice.
- **Minimal safe implementation** — the smallest path known to preserve the invariant.
- **Rejected shortcut** — the tempting approach that already failed review.
- **Evidence** — the test or checkpoint supporting the decision.
- **Claim boundary** — what the evidence does not authorize.
Removing any one of these creates a predictable failure. Without the invariant, an optimization can preserve the prose while changing the model. Without the failure signature, the note is hard to retrieve from symptoms. Without the rejected shortcut, the next agent repeats the same plausible mistake. Without the claim boundary, a structural test becomes a support claim.
## Store decisions, not progress narration
“Metal lane is 80% complete” is useful for coordination and nearly useless as durable memory. It becomes false as soon as the next commit lands.
“Only template-authored atoms may become trusted control IDs” remains useful across implementations. It can be tested, linked, superseded, and retrieved by a future agent facing a literal-token injection bug.
Durable notes should therefore be created for:
- architectural choices;
- invariants and ownership;
- rejected alternatives;
- failure and rollback semantics;
- measured thresholds;
- upstream behavior imported as local regression evidence.
Routine progress, conversation, temporary estimates, and unreviewed patches stay out of the corpus.
## Make dependencies explicit
Notes form a graph, not a timeline.
`prerequisites` tells an agent which decisions must already hold. `related_notes` offers optional context. `supersedes` prevents an older record from silently remaining authoritative after a later decision replaces it.
This is more reliable than relying on publication order. A Phase 8 quantization decision may depend on a Phase 2 artifact invariant while being unrelated to several chronologically intervening notes.
## Estimate context before loading it
The machine index includes approximate token counts for both capsule and full note. The estimate is intentionally simple—UTF-8 source bytes divided by four—and is labelled approximate.
Its purpose is not billing precision. It lets an orchestrator choose between a 200-token recovery and a 2,000-token rationale before spending the context.
When exact constants or code are needed, the note points to evidence rather than copying an entire source file into the narrative.
## A bounded handoff workflow
At a safe checkpoint:
1. record the immutable commit or evidence revision;
2. list decisions that constrain the next task;
3. state byte, file, cache, or state ownership;
4. name the one active exit gate;
5. record known failure signatures and stop conditions;
6. link only the minimum source files or upstream diffs;
7. mark explicitly what remains unsupported;
8. let the receiving agent request deeper context only when blocked.
The receiving agent should be able to answer three questions from the capsule alone:
```text
What may I change?
What must remain true?
What evidence proves I am done?
```
If it cannot, the handoff is incomplete. If answering requires the entire transcript, the project memory has not been compressed into operational knowledge.
## The general rule
Context should be progressive and evidence-linked.
Do not optimize by deleting the reasoning that future maintainers may need. Move it to a deeper retrieval tier. Do not optimize by pasting everything into every agent. Put a decision-complete capsule in front of the rationale and give the agent a trustworthy way to descend.
The goal is not the fewest possible tokens. It is the fewest tokens that preserve authority, failure boundaries, and the ability to continue without repeating settled work.
---
# AFN-008 — Close the text pipeline before asking the model
Project: Hebrus — https://github.com/andreaborio/hebrus
Contribute: https://github.com/andreaborio/hebrus/blob/main/CONTRIBUTING.md
## Retrieval capsule
- Status: verified checkpoint
- Phase: 4
- Estimated context: capsule ≈307 tokens; full note ≈1196
- Retrieve when: You are sequencing a new model-family integration and need a test hierarchy that isolates text-pipeline failures before checkpoint-backed graph parity.
- Retrieval triggers: full-model mismatch has too many possible owners; tokenizer or template fixture design; mutation-testing a model contract
- Decision: Make tokenizer and chat behavior independently executable and mutation-tested before using full-model output as the debugging oracle.
- Invariant: Prompt bytes, token IDs, errors, and trust provenance are closed before layer or final-logit parity is allowed to diagnose the graph.
- Failure signature: One differing model response leaves tokenizer, template, graph, state, quantization, kernel, and sampler defects equally plausible.
- Minimal safe implementation: Combine pinned upstream captures, independent offline regeneration, native parity tests, labelled contract controls, and fail-closed mutation testing.
- Rejected shortcut: Using a plausible full-model answer as the first oracle or letting a fixture generator approve its own implementation.
- Evidence: 59 tokenizer cases, six decode controls, 39 chat cases, 153/153 contract mutations rejected, sanitizer and full model-free regression gates
- Evidence checkpoint: Hebrus fcfd5de
- Claim boundary: These model-free gates do not contain checkpoint weights or prove layer, logits, quality, Metal, SSD, memory, or performance parity.
- Prerequisites: AFN-001
- Related notes: AFN-005, AFN-006, AFN-007, AFN-009, AFN-010, AFN-014
- Supersedes: none
- Canonical URL: https://andreabor.io/agents/oracles-before-full-model-parity/
- Markdown URL: https://andreabor.io/agents/oracles-before-full-model-parity/index.md
Full-model output is a terrible first oracle for a new runtime.
If the answer differs from Transformers, the cause may be tokenization, chat rendering, special IDs, graph math, state layout, quantization, kernels, cache transitions, or sampling. One failing string tells an agent almost nothing about which layer owns the defect.
Phase 4 of the Qwen4Exp bring-up closes the text pipeline while execution is still disabled.
## The evidence stack
The tokenizer gate contains:
```text
59 pinned text encode/decode cases
6 explicit decode-boundary controls
33 exact added-token identities
1 closed effective-vocabulary/physical-logits boundary
```
The chat gate contains:
```text
34 pinned Transformers render or error captures
5 Hebrus text-only contract rejections
39 total cases
```
Together they cover default and explicit reasoning effort, disabled thinking, preservation of prior reasoning, system-role cardinality, tools, grouped tool results, insertion-ordered UTF-8 JSON, literal control tokens, structured-media rejection, Unicode trimming, invalid inputs, and transactional output preservation.
The wider frozen Qwen4Exp contract now rejects 153 out of 153 deliberate mutations. That total spans identity, source pins, inventory, tensor roles, layer types, derived constants, PLE geometry, expert-store geometry, graph facts, admission, tokenizer, chat, and licensing boundaries. It is not presented as “153 chat tests”; it is the mutation count for the complete contract after Phase 4.
## Four different authorities
The oracle hierarchy keeps four kinds of evidence distinct:
1. **Pinned upstream captures** establish what the selected public Transformers revision does.
2. **Independent regeneration** checks that the fixture is not merely a dump produced by the implementation under test.
3. **Native C tests** establish parity and failure semantics in Hebrus.
4. **Contract controls** define behavior that upstream does not own, such as trust provenance, text-only rejection, rollback, and deterministic boundaries.
A fixture records the origin of each case. Contract controls are not labelled as upstream output, and upstream behavior is not silently replaced because a native approximation is easier.
## Why model-free first
The tokenizer and renderer need no checkpoint allocation, Metal device, SSD cache, or 64 GB machine. Their gate is fast enough to run under sanitizers and in the normal regression suite.
This changes the debugging tree for the first resident graph:
```text
prompt bytes differ? -> chat oracle
token IDs differ? -> tokenizer oracle
embedding input differs? -> graph boundary
layer output differs? -> primitive or graph implementation
final logits differ? -> graph, state, or numeric policy
sample differs only? -> sampler boundary
```
Without the first two closed nodes, every later mismatch remains ambiguous.
## Mutation tests are stronger than happy paths
A positive fixture proves one accepted object works. A mutation battery proves the validator notices which facts make it that object.
Phase 4 adds mutations for tokenizer authority, normalization, regex, added-token mappings, valid vocabulary size, physical width, stop IDs, template defaults, trimming, segment kinds, structured-media policy, oracle counts, and fixture digests.
The important property is fail-closed behavior. A changed digest cannot be accepted because the visible cases still happen to pass. A changed count cannot be ignored because the array looks plausible. A flattened trust boundary cannot hide behind byte-identical rendering.
## What the checkpoint does not claim
No weights are loaded by these tests. They do not establish:
- BF16 or quantized layer parity;
- the resident Metal graph;
- sparse QSA kernels;
- PLE SSD streaming;
- expert codec quality;
- long-context state behavior;
- memory admission;
- tokens per second;
- product or server support.
The family remains `pinned-not-supported`. The value of Phase 4 is not that the model can answer. It is that, when the first graph answers incorrectly, two entire classes of causes have already been removed from the search space.
## General rule for agents
Before building a full-model oracle:
- close prompt rendering and tokenization independently;
- pin upstream implementations and serialized assets;
- preserve exact bytes, IDs, errors, and trust metadata;
- give policy decisions their own contract-labelled cases;
- regenerate fixtures independently and offline;
- mutate every identity field and semantic boundary;
- keep support status false until later gates earn it.
The fastest autonomous implementation is not the one that reaches a plausible answer first. It is the one that makes each wrong answer cheap to localize.
---
# AFN-007 — Unicode is part of the model contract
Project: Hebrus — https://github.com/andreaborio/hebrus
Contribute: https://github.com/andreaborio/hebrus/blob/main/CONTRIBUTING.md
## Retrieval capsule
- Status: verified checkpoint
- Phase: 4
- Estimated context: capsule ≈345 tokens; full note ≈1055
- Retrieve when: You are porting a Python tokenizer or chat template into a native runtime and need to identify Unicode behaviors that disappear under reconstructed regexes, ASCII trimming, or locale-sensitive functions.
- Retrieval triggers: Python tokenizer ported to native code; combining-mark token drift; Jinja or Python trim mismatch
- Decision: Treat tokenizer.json and the captured Python/Jinja Unicode behavior as executable authority instead of approximating them with tokenizer-class defaults or locale-dependent C helpers.
- Invariant: The serialized tokenizer backend and pinned Python/Jinja byte behavior outrank convenience constructors and host-language whitespace guesses.
- Failure signature: Multilingual text produces different IDs, combining marks split differently, or edge information separators survive native trimming.
- Minimal safe implementation: Pin tokenizer.json, normalization and regex; trim complete Unicode code points with captured Python semantics; independently regenerate exact bytes and IDs.
- Rejected shortcut: Reconstructing with AutoTokenizer defaults or replacing Python/Jinja behavior with byte-wise isspace-style helpers.
- Evidence: Pinned tokenizer.json backend, Devanagari divergence capture, Unicode trimming fixture, independent offline regeneration, and C99 parity tests
- Evidence checkpoint: Hebrus fcfd5de
- Claim boundary: The oracle closes the tested tokenizer and template semantics for the pinned revision; it is not a general Unicode library or a promise about future upstream revisions.
- Prerequisites: AFN-001
- Related notes: AFN-006, AFN-008
- Supersedes: none
- Canonical URL: https://andreabor.io/agents/unicode-is-part-of-the-model-contract/
- Markdown URL: https://andreabor.io/agents/unicode-is-part-of-the-model-contract/index.md
Unicode bugs in an LLM runtime rarely announce themselves as Unicode bugs. They appear as different token IDs, a shifted prompt boundary, or a model that is inexplicably worse in one language.
Qwen4Exp exposed two separate traps during the model-free text bring-up.
## The tokenizer class was not authoritative
The checkpoint ships an exact `tokenizer.json`. Loading that file through the pinned Transformers `TokenizersBackend` produces a pre-tokenizer expression that includes combining marks:
```text
[\p{L}\p{M}]+
```
Reconstructing the tokenizer through the pinned Qwen2 `AutoTokenizer` path omitted `\p{M}` from the relevant expression. That changes how Devanagari letters and their combining marks are grouped before BPE.
Both paths look official. Both load the same repository. Only one matches the serialized tokenizer artifact.
Hebrus therefore freezes the file-backed backend as authority and records the reconstruction divergence as a negative fact:
```text
authority = tokenizer.json
AutoTokenizer authoritative = false
normalization = NFC
```
The runtime implements the exact expression rather than a nearby “GPT-style” regex.
## Trimming was wider than C whitespace
The chat template uses Jinja trimming, which delegates to Python string behavior. Python's `str.strip()` covers Unicode whitespace and also removes the ASCII information separators U+001C through U+001F at the edges.
Common native approximations are insufficient:
- `isspace()` is locale-sensitive and operates on byte-sized values;
- trimming only ASCII space, tab, CR, and LF misses valid cases;
- a Unicode White_Space table alone misses Python's information separators;
- byte-wise reverse scanning can split a multibyte code point.
The native renderer decodes UTF-8 boundaries, applies an explicit Python-compatible predicate, and trims only complete code points from the two edges.
The fixture combines Unicode whitespace and information separators in one case so that replacing the predicate with a conventional helper changes the rendered bytes.
## Why “valid UTF-8” is not enough
Two runtimes can accept the same input as valid UTF-8 and still disagree about:
- normalization;
- pre-tokenizer grouping;
- which code points count as leading or trailing whitespace;
- replacement behavior for invalid sequences;
- byte fallback;
- embedded NUL handling.
Every disagreement can change the prompt token sequence. Full-model parity cannot repair a text pipeline that feeds different IDs into layer zero.
The tokenizer oracle therefore includes multilingual text, combining marks, invalid UTF-8 replacement, embedded NUL, special-token literals, and raw-versus-trusted control behavior. The chat oracle separately freezes trimming and serialized output bytes.
## Capture upstream, check offline
The golden vectors are captured with pinned versions of Python, Transformers, tokenizers, Jinja, and the checkpoint files. Provenance records the public source hashes and package versions.
Normal testing does not depend on the network or those packages. An independent standard-library transcription regenerates the canonical fixture offline, and the C implementation must match the frozen outputs.
This gives two useful failure modes:
- upstream capture drift says the external authority changed;
- offline or C drift says the local transcription changed.
Conflating the two would make a fixture refresh capable of approving its own bug.
## General rule for agents
When porting a tokenizer or template across languages:
1. identify the serialized artifact that actually defines behavior;
2. record any disagreement with convenience constructors;
3. freeze normalization, regex, trimming, and invalid-byte behavior explicitly;
4. include scripts and languages that require combining marks;
5. include code points at the edge of each host language's whitespace rules;
6. compare token IDs and exact rendered bytes, not only decoded text;
7. keep the upstream capture path separate from the offline checker.
“Unicode support” is not one checkbox. For an LLM, Unicode preprocessing is part of the numerical function because it decides which integers enter the graph.
---
# AFN-006 — Rendered chat is not a flat string: preserve provenance
Project: Hebrus — https://github.com/andreaborio/hebrus
Contribute: https://github.com/andreaborio/hebrus/blob/main/CONTRIBUTING.md
## Retrieval capsule
- Status: verified checkpoint
- Phase: 4
- Estimated context: capsule ≈315 tokens; full note ≈1045
- Retrieve when: You are implementing a chat template with special tokens and need to prevent user, system, or tool text from being reinterpreted as template-authored control syntax.
- Retrieval triggers: chat template mixes controls with client text; literal special-token injection; tool output contains protocol markers
- Decision: Render chat as one byte stream plus contiguous DATA and TRUSTED_CONTROL segments, then tokenize every segment according to its provenance.
- Invariant: Only template-authored atoms can become trusted control IDs; caller data remains data even when its bytes spell the same token.
- Failure signature: Flattening and retokenizing promotes a user, system, or tool literal such as <|im_end|> into template syntax.
- Minimal safe implementation: Return one byte stream plus complete contiguous provenance segments, validate coverage, and tokenize DATA and TRUSTED_CONTROL independently and transactionally.
- Rejected shortcut: Escaping suspicious characters or flattening the rendered transcript and trying to reconstruct trust from its spelling.
- Evidence: 39-case chat oracle, literal-control adversarial cases, segmented tokenizer adapter, transactional failure tests, and the Phase 4 model-free gate
- Evidence checkpoint: Hebrus fcfd5de
- Claim boundary: The segmentation boundary prevents control-token promotion in the text pipeline; it does not sanitize tool behavior or grant runtime support to the model.
- Prerequisites: AFN-001
- Related notes: AFN-007, AFN-008
- Supersedes: none
- Canonical URL: https://andreabor.io/agents/chat-rendering-needs-provenance/
- Markdown URL: https://andreabor.io/agents/chat-rendering-needs-provenance/index.md
A rendered chat transcript looks like a string. Treating it as only a string is a security bug.
Consider the bytes:
```text
<|im_end|>
```
When the template emits them, they delimit a message. When a user types the same bytes inside a question, they are ordinary content. The spelling is identical; the authority is not.
If a renderer concatenates everything and then runs one trusted tokenizer pass, client text can acquire the semantics of template syntax. The runtime has erased the only information that could prevent promotion.
## The representation
The Qwen4Exp chat renderer returns:
1. one ordered UTF-8 byte stream for inspection and logging;
2. a complete, gap-free sequence of segments over those bytes;
3. one kind for each segment: `DATA` or `TRUSTED_CONTROL`.
Adjacent segments of the same kind are merged. Every segment must be non-empty, begin exactly where the previous one ends, and collectively cover the full stream.
Template-authored markers such as these are controls:
```text
<|im_start|>
<|im_end|>
```
Conversation content remains data, even if it contains any of those literal spellings.
## Tokenize the segments, not the flattened bytes
The adapter validates the segment geometry first. It then tokenizes each contiguous segment independently:
- `DATA` goes through the ordinary NFC, pre-tokenizer, and BPE path;
- `TRUSTED_CONTROL` may resolve exact special-token atoms.
The resulting token arrays are appended transactionally. If a segment is malformed, a control is unknown, UTF-8 handling fails, or allocation fails, the caller's prior token output is preserved.
Flattening the bytes and tokenizing them again as trusted is forbidden. The rendered string is useful for observability; it is not a substitute for the provenance stream.
## Why escaping is the wrong abstraction
One possible response is to escape `<`, `>`, or the pipe character in client text. That changes user content, code snippets, shell output, and tool results. It also assumes every dangerous control can be recognized with a lexical filter.
Provenance is stronger because it does not depend on guessing whether data looks suspicious. Data stays data by construction.
This is especially important for tool output. A tool may legitimately return source code or model text containing closing tags and chat markers. The renderer must preserve the bytes without granting them template authority.
## Adversarial cases
The Phase 4 oracle includes literal controls in:
- user content;
- system content;
- tool results;
- structured text that spells a media token.
The tests assert that the client occurrence remains `DATA` while the actual template delimiter is `TRUSTED_CONTROL`. The tokenizer integration then proves that only the latter becomes a special ID.
Structured image, image-URL, and video parts are rejected before rendering because the current artifact is text-only. A literal `<|image_pad|>` inside normal text remains legal data; it does not smuggle a media part into the request.
## Transactionality matters here too
Chat rendering allocates a replacement output and commits only after full validation and rendering. An invalid request does not partially overwrite a previously valid transcript or segment array.
This turns errors such as a second system message, an unsupported role, malformed tool arguments, or structured media into clean failure states. Downstream code never receives a half-rendered trust map.
## General rule for agents
Whenever a protocol mixes control syntax and caller-controlled text:
- carry provenance beside bytes until semantic parsing is complete;
- do not recover provenance by rescanning the flattened representation;
- validate complete, contiguous ownership of the byte stream;
- make trust promotion an explicit API, not a tokenizer heuristic;
- preserve caller outputs on rejection;
- test identical spellings under both trusted and untrusted origins.
This is not a Qwen-specific trick. It is a general rule for structured prompts: syntax is not defined by appearance alone. It is defined by who was allowed to create it.
---
# AFN-005 — A logits matrix is not always the vocabulary
Project: Hebrus — https://github.com/andreaborio/hebrus
Contribute: https://github.com/andreaborio/hebrus/blob/main/CONTRIBUTING.md
## Retrieval capsule
- Status: verified checkpoint
- Phase: 4
- Estimated context: capsule ≈292 tokens; full note ≈937
- Retrieve when: You are integrating a checkpoint whose output projection is padded beyond the tokenizer vocabulary and need exact rules for sampling, decoding, stopping, and artifact admission.
- Retrieval triggers: output rows exceed tokenizer vocabulary; padded logits produce invalid IDs; sampling and decode boundaries disagree
- Decision: Represent the tokenizer domain and the physical logits width as separate constants, masking every unassigned output row before sampling.
- Invariant: The graph admits 248,320 physical rows while sampling remains restricted to token IDs in [0, 248077).
- Failure signature: An unassigned padded row wins selection and is clamped, decoded ambiguously, or misinterpreted as a stop token.
- Minimal safe implementation: Store physical width and semantic vocabulary separately, mask every invalid row before selection, and test with invalid rows numerically dominant.
- Rejected shortcut: Using one n_vocab value to answer tensor shape, sampling validity, decode behavior, and stop semantics.
- Evidence: Pinned tokenizer backend, 59 encode/decode cases, six boundary controls, sampler-boundary test, and the Phase 4 model-free gate
- Evidence checkpoint: Hebrus fcfd5de
- Claim boundary: This closes the text-domain semantics only; it does not prove full-model logits parity, generation quality, or runtime support.
- Prerequisites: AFN-001
- Related notes: AFN-004, AFN-008
- Supersedes: none
- Canonical URL: https://andreabor.io/agents/physical-logits-are-not-the-vocabulary/
- Markdown URL: https://andreabor.io/agents/physical-logits-are-not-the-vocabulary/index.md
The number of rows in an output projection is a physical shape. The number of tokens a tokenizer can produce is a semantic domain. They are often equal, which makes it easy for runtime code to treat them as the same fact.
For Qwen3.8-Flash-Next, they are not equal:
```text
physical output rows = 248,320
effective tokenizer IDs = 248,077
last valid token ID = 248,076
unassigned physical rows = [248,077, 248,320)
unassigned row count = 243
```
Hebrus keeps both values in the model contract. One may not be derived from the other.
## Why the extra rows are dangerous
The padded rows are real matrix rows. A graph must produce values for them, and a loader must admit the physical tensor shape. But those rows do not name tokens.
If a sampler uses the physical width as its candidate count, an unassigned row can win argmax or enter top-k. The runtime then has no legitimate token to append. Silently clamping the ID, decoding it as replacement text, or treating it as EOS would all create a new model behavior.
The sampling rule is therefore explicit:
```text
sample IDs in [0, 248077)
mask logits IDs in [248077, 248320)
```
Masking happens before any selection policy. It is not a decoder fallback after an invalid ID has already won.
## Decode and stop are separate policies
The pinned tokenizer backend decodes the 243 unassigned physical IDs as empty. Hebrus preserves that boundary behavior for defensive decoding, but decode-silent does not mean sample-valid.
The stop set is also exact:
```text
248044 <|endoftext|>
248046 <|im_end|>
```
`<|im_start|>` at 248045 is not a stop token. The model configuration's EOS declaration and the tokenizer's chat EOS are not collapsed into one guessed value; the runtime records the effective stop set deliberately.
This creates four distinct questions for any ID:
1. Does a physical output row exist?
2. Is the ID valid for sampling?
3. Does the tokenizer have decode semantics for it?
4. Does generation stop after it?
Code that answers all four from a single `n_vocab` integer is underspecified for this checkpoint.
## The test that matters
The boundary test gives every invalid physical row a higher score than every valid row. The last valid ID, 248076, is still required to win after masking.
That test catches the implementation error directly. A fixture that merely checks constants would not prove the sampler uses the semantic domain.
Decode controls independently cover the first and last unassigned rows, a mixed valid/unassigned sequence, negative IDs, and values beyond the physical width. This prevents a future cleanup from accidentally turning a defensive decode behavior into sampler authority.
## Admission implications
Artifact validation needs both equalities:
- the output tensor must have 248,320 rows;
- the tokenizer payload must define exactly 248,077 effective IDs.
An artifact with a 248,077-row output matrix is not a compact equivalent. An artifact with 248,320 declared tokenizer IDs is not the same tokenizer. Either change requires a distinct physical profile and new evidence.
## General rule for agents
When a checkpoint uses a padded output projection:
- name physical and semantic dimensions separately;
- validate each against its own source of authority;
- mask unassigned rows before top-k, top-p, argmax, or temperature logic;
- make invalid decode behavior explicit but never use it to authorize sampling;
- freeze the stop set independently from BOS, PAD, and generic EOS fields;
- test with invalid rows deliberately made numerically dominant.
The broader lesson is simple: tensor geometry tells a runtime what it must compute. A tokenizer contract tells it what those results are allowed to mean.
---
# AFN-001 — Porting Qwen3.8-Flash-Next to Hebrus: evidence before execution
Project: Hebrus — https://github.com/andreaborio/hebrus
Contribute: https://github.com/andreaborio/hebrus/blob/main/CONTRIBUTING.md
## Retrieval capsule
- Status: verified checkpoint
- Phase: 0–3
- Estimated context: capsule ≈292 tokens; full note ≈1414
- Retrieve when: You are adding a new model family to an inference runtime and need an order of operations that prevents a plausible but incorrect graph from becoming the de facto specification.
- Retrieval triggers: new model-family bring-up; uncertain checkpoint identity; graph work beginning before executable contracts
- Decision: Freeze the source identity and executable contracts before implementing model execution.
- Invariant: Execution cannot become authoritative until source identity, model math, artifact geometry, and admission failures are independently executable.
- Failure signature: Kernel debugging begins before the exact input contract is closed, so local fixes accumulate around a plausible but incorrect model.
- Minimal safe implementation: Pin source and inventory, build scalar oracles, freeze physical owners, and require CPU-only admission before creating the execution graph.
- Rejected shortcut: Starting from a model name or generic architecture match and allowing the first plausible graph to define the specification.
- Evidence: Model-free, sanitizer, admission, CPU, Metal-regression, and build-isolation gates
- Evidence checkpoint: Hebrus 6c3ae19
- Claim boundary: The checkpoint is structurally admitted but remains pinned-not-supported; this note makes no throughput, quality, or runtime-support claim.
- Prerequisites: none
- Related notes: AFN-002, AFN-003, AFN-004, AFN-008, AFN-009
- Supersedes: none
- Canonical URL: https://andreabor.io/agents/evidence-before-execution/
- Markdown URL: https://andreabor.io/agents/evidence-before-execution/index.md
The fastest way to make a new model family difficult to debug is to start with the graph.
Once tensors are flowing through kernels, every mismatch looks like a kernel problem. A shifted layer pattern looks like bad attention. A wrong norm convention looks like quantization damage. A tokenizer drift looks like incoherent logits. The implementation becomes a pile of plausible local fixes around an identity that was never closed.
For the Qwen3.8-Flash-Next integration, Hebrus took the opposite route: execution was forbidden until the source, equations, artifacts, and admission failures were independently testable.
## The object being integrated
The exact target is the public checkpoint [`Qwen/Qwen3.8-Flash-Next`](https://huggingface.co/Qwen/Qwen3.8-Flash-Next) at revision `de4b8e4d43b917e7706784d8bb445c9af86a3540`.
The closed text profile has:
| Property | Exact value |
|---|---:|
| Architecture | `Qwen4ExpForConditionalGeneration` |
| Runtime family spelling | `qwen4exp` |
| Text layers | 48 |
| Hidden width | 2,560 |
| Residual streams | 4 |
| Layer pattern | GDN, GDN, GDN, QSA; repeated 12 times |
| Routed experts | 512 |
| Active experts/token | 10 |
| Expert width | 640 |
| Maximum context | 262,144 tokens |
| Source tensors | 1,658 |
| Source shards | 131 |
| Indexed source bytes | 359,999,963,128 |
These values are admission equalities, not hints. A model with a similar name or dimensions is a different model.
## Phase 0: make identity executable
The source revision, tokenizer, chat template, configuration, Transformers implementation, shard ownership, tensor inventory, license, and file digests were pinned first.
The important output was not prose. It was a contract that can reject mutations. If an agent changes one prime, one layer type, one dtype, one shard span, or one source hash, the validator must fail on that field.
This turns “we are implementing Qwen4Exp” into a finite statement:
```text
repository + revision
+ exact file digests
+ exact tensor inventory
+ exact configuration
+ exact exclusions
= one admissible source identity
```
The first artifact is text-only. The 333 vision tensors and 31 MTP tensors in the source are accounted for but excluded by named rules. They are not silently ignored, and the base runtime is not allowed to accept image/video input or execute MTP.
## Phase 1: separate math from acceleration
Allocation-free C references were added before Metal work for:
- zero-centred RMS normalization;
- four-stream gated residual mixing;
- Gated DeltaNet recurrence and convolution state;
- float32 router softmax and normalized top-10 selection;
- Qwen sparse-attention grouping and deterministic selection;
- PLE hashing, gating, convolution, and history transitions.
The fixtures are captured from a pinned Transformers commit, then cross-checked against an independent transcription. Contract-only cases—tie policy, integer wrap, reset/copy/rewind—are labelled as such rather than misrepresented as upstream output.
This matters because several operations are unusually easy to “optimize” into a different model. Examples include adding one to the wrong norm weight, reassociating the GDN recurrence, pooling physical cache slots rather than logical token groups, or changing tie order in sparse selection.
## Phase 2: freeze physical structure without choosing a winner
The artifact has three disjoint owners:
1. dense/non-routed tensors;
2. one embedded `ds4.expert_major.v2` routed-expert store;
3. one embedded `ds4.ple_rows.v1` row store.
ExpertMajor retained its v2 container. A new family ID closes the 48-layer, 512-expert, gate/up/down geometry without reviving an abandoned v3 format.
The PLE table is not disguised as experts. It has a different access pattern, checksum unit, cache policy, and future codec decision, so it received a separate fixed-page format.
Crucially, the structural phase did not pretend to have selected production quantization. MLX affine4 with group size 64 is sufficient to prove the ExpertMajor geometry, but it is explicitly marked `phase2-structural-not-release-qualified`. The PLE production codec remains open.
## Phase 3: reject before the GPU exists
The structural admission binary is deliberately CPU-only. It validates the family, profile, source hashes, model constants, layer array, tokenizer/template identity, exact tensor set, embedded store manifests, byte extents, ownership, and text-only policy.
Only after those checks may a future runtime allocate pipelines, graph state, or caches. Negative fixtures independently mutate fields and assert that rejection happens at the intended boundary.
This ordering makes the failure useful. “Wrong `ple.rows`” is actionable. “The model emitted nonsense after 40 seconds” is not.
## The implementation rule for other agents
When continuing this port:
1. Read the current contract, not the model name.
2. Preserve `pinned-not-supported` until execution, quality, memory, and performance gates pass.
3. Treat every new optimization as a hypothesis behind an oracle.
4. Keep one owner for every state buffer and physical byte range.
5. Never weaken existing model-family contracts to admit the new family.
The checkpoint through structural admission is recorded as Hebrus commit `6c3ae19`. The public repository is [`andreaborio/hebrus`](https://github.com/andreaborio/hebrus); the note keeps the exact checkpoint even when a working branch has not yet been published upstream.
The result is not yet a running model. It is something more useful at this stage: a model that cannot quietly become the wrong model while agents implement it.
---
# AFN-002 — Why Qwen4Exp kept ExpertMajor v2
Project: Hebrus — https://github.com/andreaborio/hebrus
Contribute: https://github.com/andreaborio/hebrus/blob/main/CONTRIBUTING.md
## Retrieval capsule
- Status: verified structural decision
- Phase: 2
- Estimated context: capsule ≈288 tokens; full note ≈1045
- Retrieve when: You need to add a model with new expert geometry to an existing routed-expert container without conflating model family, storage format, and release-qualified codec.
- Retrieval triggers: new expert geometry in an existing container; logical row width does not divide codec block size; structural acceptance confused with codec qualification
- Decision: Extend ExpertMajor v2 with a distinct family-4 descriptor while keeping its wire format unchanged.
- Invariant: Container version, model-family geometry, and physical codec identity remain independent decisions.
- Failure signature: A structurally plausible store parses even though one role cannot be represented by the declared block geometry, or a new family changes legacy output bytes.
- Minimal safe implementation: Add one immutable family descriptor, validate every count/role/shape/order exactly, preserve legacy branches, and label a geometry-only codec candidate as non-release.
- Rejected shortcut: Bumping the wire version, padding width 640 opportunistically, or reusing another family ID merely to obtain a positive fixture.
- Evidence: Sparse 48×512 fixture, unchanged legacy fixture hashes, strict C and sanitizer tests
- Evidence checkpoint: Hebrus c5cedb0
- Claim boundary: MLX affine4 G64 proves the geometry only; no Qwen4Exp routed-expert codec is release-qualified by this decision.
- Prerequisites: AFN-001
- Related notes: AFN-003, AFN-004
- Supersedes: none
- Canonical URL: https://andreabor.io/agents/expertmajor-family-four/
- Markdown URL: https://andreabor.io/agents/expertmajor-family-four/index.md
Adding 512 experts did not require a new container. It required a new family contract.
That distinction sounds small, but it prevents two common mistakes in artifact work:
1. incrementing a format version when only the admitted geometry changed;
2. reusing a family ID because the byte layout happens to parse.
Hebrus already had `ds4.expert_major.v2`: a family-tagged, checksummed store that keeps routed gate/up/down records contiguous by expert. Qwen4Exp needs the same physical idea but has different model invariants.
## The closed family-4 geometry
The Qwen4Exp descriptor requires:
```text
layers = 48
experts/layer = 512
experts used = 10
components = gate | up | down
gate = [2560, 640, 512]
up = [2560, 640, 512]
down = [640, 2560, 512]
layer IDs = 0..47, contiguous
minimum alignment = existing ExpertMajor v2 rules
```
The v2 header and record layout did not need to change. The parser needed a fourth immutable descriptor and a higher structural maximum: 384 to 512 experts.
Families 1–3 continue through their previous branches. Their regression artifacts remain byte-identical. Family 4 is admitted only when every exact count, component role, dimension, order, offset, digest, and storage rule matches.
## The 640-wide row problem
The down projection has logical row width 640. Existing admitted GGML K-quant layouts use 256-element blocks.
`640 % 256 != 0`.
That is not a tail to hand-wave away. The container descriptor describes exact physical records. If the codec cannot represent the logical row under its closed block rules, the builder must fail.
The structural fixture therefore uses the already-understood MLX affine4 representation with group size 64:
```text
group elements = 64
group bytes = 36
640 / 64 = 10 exact groups
```
This establishes that the v2 record geometry, extents, offsets, and reader can represent the family. It does **not** establish that affine4 G64 has acceptable quality or is the best runtime codec.
The implementation labels it accordingly:
```text
phase2-structural-not-release-qualified
```
## Why not invent padding?
Padding 640 to 768 would make a 256-element block codec fit, but it would also create a new physical profile with new byte formulas, kernel behavior, quality implications, and manifest semantics.
That may eventually be a valid candidate. It is not a parser fix.
An autonomous agent should never introduce that kind of padding locally just to get a positive fixture. The correct sequence is:
1. define the candidate codec and padding rule;
2. give it a distinct profile identity;
3. implement checked byte formulas and tail semantics;
4. add decoder/kernel parity tests;
5. measure model quality and target-hardware performance;
6. promote only after evidence.
## Why not call the PLE table 512 more experts?
Because representation should follow access geometry.
Routed experts are selected in small sets and consumed as matrices. PLE is a 320-million-row lookup table with page locality, hash-derived row IDs, independent checksums, and a future SSD page cache. Giving both the same container would save a type name and destroy ownership clarity.
The artifact therefore contains one ExpertMajor extent and one PLE extent. Their spans must be disjoint, and neither may be accidentally included in dense warm-up ranges.
## Agent checklist
When adding another ExpertMajor family:
- change the family descriptor before touching the shared parser;
- preserve legacy branches and verify old output hashes;
- distinguish structural parser acceptance from builder availability;
- reject a codec whose block geometry does not exactly cover every role;
- avoid padding, transposition, or logical-type aliases without a named profile;
- keep support status separate from “the fixture parses”.
The general lesson is that a stable container is valuable precisely because new model geometry can be admitted without changing its wire format. Version the structure when the structure changes. Use a family/profile identity when the model contract changes. Use a codec identity when the bytes change.
---
# AFN-003 — 320 million embedding rows without a 320 million-entry index
Project: Hebrus — https://github.com/andreaborio/hebrus
Contribute: https://github.com/andreaborio/hebrus/blob/main/CONTRIBUTING.md
## Retrieval capsule
- Status: verified structural decision
- Phase: 2
- Estimated context: capsule ≈284 tokens; full note ≈1146
- Retrieve when: You are designing storage for hundreds of millions of fixed-width lookup rows and need bounded metadata, independent verification, and future SSD caching.
- Retrieval triggers: hundreds of millions of fixed-width rows; bounded SSD lookup metadata; TOCTOU-safe page verification
- Decision: Represent PLE as a fixed-page embedded extent with affine row location and one digest per page, never a per-row offset table.
- Invariant: Every row maps affinely to one fixed page, and caller output is published only from the same verified page snapshot.
- Failure signature: Metadata grows with row count, lazy mapping creates unbounded first-touch, or bytes are verified in one read and copied from another.
- Minimal safe implementation: Use a fixed manifest, one digest per fixed-stride page, checked locate arithmetic, single-snapshot verified reads, and an atomic verified writer.
- Rejected shortcut: A per-row offset table, OS page faults as the scheduler, or a two-read verify-then-copy path.
- Evidence: Strict parser/writer tests, ASan/UBSan, whole-payload corruption, overflow, fsync, rename, and short-read failures
- Evidence checkpoint: Hebrus c5cedb0
- Claim boundary: The wire structure is frozen; production row codec, rows per page, page stride, and cache policy remain unqualified.
- Prerequisites: AFN-001
- Related notes: AFN-002, AFN-004, AFN-012
- Supersedes: none
- Canonical URL: https://andreabor.io/agents/ple-fixed-page-store/
- Markdown URL: https://andreabor.io/agents/ple-fixed-page-store/index.md
Qwen3.8-Flash-Next includes a large PLE table: 320,001,536 rows, each 160 values wide.
A naïve runtime design reaches for an offset table. At eight bytes per row, that index alone would be roughly 2.38 GiB before allocator and page overhead. It would also solve a problem that does not exist: the rows are fixed-size inside a closed codec profile.
The PLE v1 design uses fixed physical pages and affine arithmetic instead.
## Logical geometry
The sixteen hash heads use cumulative prime-sized segments. Their exact logical end is:
```text
head rows = 320,001,446
```
The checkpoint pads this to a row alignment of 128:
```text
align_up(320,001,446, 128) = 320,001,536
padding rows = 90
```
The 90 rows matter. Requiring the prime segments to sum to the padded extent would make the real checkpoint impossible to admit. Treating the trailing rows as arbitrary slack would weaken the identity. The manifest closes both values and the alignment rule.
## Physical geometry
PLE v1 has:
- a fixed 512-byte little-endian manifest header;
- a compact table with one 32-byte SHA-256 digest per page;
- alignment padding before payload;
- fixed-stride physical pages;
- a 64-byte duplicated page header;
- no per-row index.
For row `r`:
```text
page = r / rows_per_page
slot = r % rows_per_page
page_offset = payload_offset + page * page_stride
row_offset = page_header_bytes + slot * encoded_row_bytes
```
Every multiply, add, alignment, extent, and `off_t` conversion is checked before I/O.
The page header repeats enough geometry to detect a page copied from another store or position: page index, first row, valid row count, logical width, encoded row bytes, rows per page, codec version, codec group size, and family.
## Digest domains
Three digest levels have different jobs:
1. **Manifest digest** — authenticates the header with its own digest field zeroed, the page-digest table, and alignment padding.
2. **Page digest** — authenticates one complete fixed-stride physical page, including its header and unused stride bytes.
3. **Payload digest** — authenticates every complete physical page in order.
Opening the store validates structure and the manifest without reading hundreds of millions of rows. Offline publication runs every page digest and the whole-payload digest.
The test for the final payload comparison is deliberately isolated: mutate payload bytes, recompute the affected page digest and manifest digest, leave the whole-payload digest stale, then require `open` and `verify_page` to pass while `verify_all` fails specifically on the whole payload.
## Transactional row reads
A row read takes one page snapshot.
It does not verify a page and then issue a second direct read for the row. That two-read design creates a time-of-check/time-of-use window: the bytes copied to the caller may not be the bytes that were hashed.
The correct sequence is:
```text
pread complete page into bounded buffer
verify duplicated page header
verify page SHA-256
copy requested encoded row to caller
```
On corruption or short read, the caller's output remains unchanged.
## Atomic publication
The writer uses a sibling temporary file and bounded one-page memory:
```text
create sibling temp
write header, digest table, padding, and pages
fsync temp file
close and reopen through the runtime parser
verify complete payload and boundary pages
fsync parent directory
rename temp over target <- commit point
fsync parent directory again
```
A failure before rename preserves the old target and removes the temporary file. A post-rename directory-fsync error is reported, but the writer does not attempt an unsafe rollback after the new complete file has become visible.
Deterministic test hooks inject fsync failure before the commit point. Rename failure is tested against a non-replaceable target. Both prove that no apparently valid partial target or sibling temp remains.
## What remains open
The structural format does not pick a production codec.
Codec ID, version, group size, encoded row bytes, rows per page, page alignment, and derived stride belong to a future artifact profile. They need quality, capacity, cold-read, cache, and Metal decode evidence on the target machine.
This separation is intentional. The storage invariants are stable:
- fixed pages;
- affine lookup;
- exact geometry;
- bounded metadata;
- independent verification;
- transactional publication.
The performance choices are not yet stable. Freezing both at once would turn an early benchmark guess into a wire-format promise.
---
# AFN-004 — Admission before Metal: make every bad artifact fail cheaply
Project: Hebrus — https://github.com/andreaborio/hebrus
Contribute: https://github.com/andreaborio/hebrus/blob/main/CONTRIBUTING.md
## Retrieval capsule
- Status: verified checkpoint
- Phase: 3
- Estimated context: capsule ≈291 tokens; full note ≈1334
- Retrieve when: You are integrating a complex artifact into a GPU runtime and need deterministic field-level failures before expensive allocation or opaque backend errors.
- Retrieval triggers: GPU loader fails too late; artifact ownership or extent ambiguity; need deterministic field-level rejection
- Decision: Complete exact artifact admission in a dedicated CPU-only path before registering Qwen4Exp execution.
- Invariant: No GPU symbol, allocation, graph, or sparse-payload read is reachable until identity, policy, tensors, extents, and ownership all pass.
- Failure signature: A malformed artifact reaches the backend and fails as an opaque missing-tensor, allocation, overlap, or device error.
- Minimal safe implementation: Run ordered CPU-only admission over exact metadata, tensor identities, manifests, spans, exclusions, and owners, with one negative fixture per boundary.
- Rejected shortcut: Treating a loader as successful once it finds enough familiar tensors to start building a graph.
- Evidence: Sparse GGUF fixture, field-specific negative matrix, GPU-symbol isolation, sanitizer and full regression gates
- Evidence checkpoint: Hebrus 6c3ae19
- Claim boundary: The dedicated binary proves structural admission only and intentionally rejects generation; normal builds still register no Qwen4Exp physical runtime profile.
- Prerequisites: AFN-001, AFN-002, AFN-003
- Related notes: AFN-005, AFN-008, AFN-013
- Supersedes: none
- Canonical URL: https://andreabor.io/agents/admission-before-metal/
- Markdown URL: https://andreabor.io/agents/admission-before-metal/index.md
A loader is not successful because it can find enough tensors to start a graph.
It is successful when it can prove that one exact artifact owns every byte and satisfies every policy before expensive or irreversible work begins.
For Qwen4Exp, Hebrus added a dedicated structural-admission binary before adding execution. The binary is CPU-only and test-hook-only. It admits one sparse miniature fixture, emits a machine-readable ownership report, and refuses generation.
Normal builds still register no Qwen4Exp physical profile.
## Ordered failure
Admission is intentionally ordered from cheap identity checks to deeper physical verification:
1. parse GGUF bounds with checked arithmetic;
2. require architecture and artifact profile IDs;
3. require source revision and inventory digests;
4. validate every scalar and array model constant;
5. bind tokenizer, template, vocabulary, regex, and special IDs;
6. classify every tensor as base, PLE, MTP, or vision;
7. require the exact text-artifact tensor identity set;
8. validate the ExpertMajor manifest and all 48×512 records;
9. validate the PLE manifest, geometry, hash identity, and extent;
10. enforce text-only and MTP-not-executed policy;
11. sort physical ranges and prove disjoint ownership;
12. only then permit future platform and memory admission.
The order is part of the interface. A bad family should not fail later as a missing tensor. An overlapping extent should not survive until an SSD read. A vision tensor in a text-only artifact should not become ignored padding.
## Exact metadata, including arrays
The loader does not infer identity from a few familiar dimensions.
It validates the 48-entry layer-type array, PLE primes and offsets, 64-bit hash seeds, rotary sections, sparse-index parameters, GR dimensions, router rules, source revision, tokenizer digest, template digest, and explicit exclusions.
Metadata type is also closed. An integer with the same printed value is not accepted when the schema requires an unsigned 64-bit value or an array of a specific element type.
This protects against a subtle class of converter bugs: values that survive JSON or command-line inspection but change representation in GGUF.
## One tensor, one role, one owner
The source inventory has four classifications:
| Class | Source tensors | Base-artifact policy |
|---|---:|---|
| Base | 1,294 | Required, subject to routed/PLE physical replacement rules |
| PLE | 137 | Collapsed into one embedded PLE extent |
| Vision | 333 | Explicitly excluded from text profile |
| MTP | 31 | Explicitly excluded and not executed |
The converter dry run consumes all 1,658 source identities exactly once. “Skipped” is legal only as a named exclusion whose bytes remain accounted.
At runtime, dense tensors, the ExpertMajor tensor, the PLE tensor, GGUF metadata, alignment padding, and file bounds are separate owners. Sorted spans must not overlap.
Page-rounded dense warm-up spans receive an additional check: they must not accidentally include the adjacent PLE payload. Otherwise a harmless-looking residency optimization could fault a huge lookup table into the primary working set.
## Negative fixtures as an API
The compact fixture generator can mutate one field at a time:
- family or profile;
- revision or digest;
- context, head dimensions, top-k, expert count, GR rank;
- layer pattern;
- one PLE prime, offset, row count, width, or hash version;
- tokenizer/template hash or special ID;
- tensor name, rank, shape, type, offset, or length;
- overlapping extents;
- missing/extra tensors;
- second store;
- canonical routed or PLE tensors that should have been replaced;
- vision/MTP policy;
- embedded manifest or page geometry.
Each negative asserts the intended diagnostic and proves that the process has not reached GPU allocation.
This is more than test coverage. It makes error ordering a stable contract for future agents. If a refactor changes “profile mismatch” into “tensor missing”, the test explains that the loader stopped failing at the cheapest authoritative boundary.
## Why the binary has no GPU symbols
“We do not call Metal” is weaker than “Metal is not link-reachable.”
The structural test binary is linked without GPU entry points, and the test inspects its unresolved symbols. That turns pre-GPU rejection from a control-flow intention into a build property.
It also keeps sanitizer runs small and deterministic. Artifact validation can be fuzzed without initializing devices, compiling kernels, or depending on host GPU state.
## Handoff to execution
The admission report includes exact owners and byte ranges but marks runtime and payload support false. Generation exits with a structural-admission-only diagnostic.
The future resident graph may consume the admitted descriptor. It may not reinterpret the file, guess a family, discover a sidecar, or weaken an equality because a kernel prefers a different shape.
That is the boundary:
```text
loader proves what the artifact is
graph proves that implementation matches the model
benchmarks prove whether the implementation is useful
release gates decide whether anyone may call it supported
```
Keeping those proofs separate makes autonomous work faster. Agents can change one layer of evidence without silently granting authority to the next.