twanghcmut/backup-VR-SmallVLA / onf-c5 /docs /technical /06-config-reference.md
twanghcmut's picture
|
download
raw
24.2 kB
# 06. Config reference
**Reads:** nothing — reference chapter
**Writes:** nothing
**Code:** `src/onf/graph/core/schema.py`, `src/onf/config.py`, `evals/common/modes.sh`
**Stage:** BUILD and RUNTIME
**Read after:** [`05-artifacts.md`](05-artifacts.md)
Every constant and every environment variable, organised by subsystem. Two kinds of knob:
| kind | where it lives | changed by |
|---|---|---|
| **frozen** | `src/onf/graph/core/schema.py`, or a module-level constant in the owning module | editing the source and rebuilding the artifact |
| **env** | a `from_env()` factory in `src/onf/config.py`, or one of the four documented exceptions | exporting a variable before the run |
`schema.py`'s flat names (`COARSEN`, `HIST_H`, …) are **aliases derived from `DEFAULTS`**, never
literals. Change the dataclass default; the flat name follows.
`GraphConfig` / `SentinelConfig` mirror many of `schema.py`'s values as their
*defaults* and expose them as `GR_*` / `SN_*` variables. A knob listed as env therefore has
two ways to move: edit `schema.py` (changes the default everywhere, including training) or export the
variable (changes this run only). The provenance column below describes the shipped value.
Provenance is `CHOSEN` (picked, no derivation on record), `DERIVED` (computed or measured from
something else, with the derivation stated), or `--` (not determinable from the code or the existing
docs — not guessed).
---
## 6.1 Graph build
Frozen in `schema.py`. Every one of these invalidates `g_nodes.npz` / `g_edges.npz`, and therefore
`graph_hash`, and therefore every head and kernel fit against them.
| constant | value | env | C/D | what breaks if it changes |
|---|---|---|---|---|
| `COARSEN` | 5 | `GR_COARSEN` | CHOSEN | raw frames per node; every node id renumbers |
| `PSI_FREQS` | 4 | — | -- | time-encoding octaves; `node_in_dim = 15 + 2·psi_freqs`, so the head's input layer width |
| `GRIP_OPEN_THR` | 0.035 | — | CHOSEN | closed-gripper threshold; moves every coarsening cut and every `grip` label |
| `PHASE_CONV` | `"arange(T)/(T-1) full"` | — | CHOSEN | stamped into `g_nodes.npz` and re-checked on load; a mismatch raises |
| `DILATIONS` | (1,2,4,8,16) | — | DERIVED | `3 × 16 × 5 = 240` raw frames of reach ≈ p50 `long` demo length |
| `RELATIONS` / `REL_INDEX` / `N_RELATIONS` | 12 names, order load-bearing | — | DERIVED | from `DILATIONS`; `rel` is an index into this order, so reordering silently re-labels every edge |
| `K_SIBLING` | 8 | `GR_K_SIBLING` | CHOSEN | kNN out-degree of `sibling`; also the lateral-mixing density the tracker's `M_sib` inherits |
| `K_ALIGN` | 4 | `GR_K_ALIGN` | CHOSEN | kNN out-degree of `align` |
| `NBINS_ALIGN` | 20 | — | CHOSEN | one bin = 0.05 phase; shared by `align` bucketing, the phase-CE loss, basin cells and the sentinel's end-of-task gate |
| `CDIST_BS` | 2048 | — | CHOSEN | `torch.cdist` row-block size; memory/throughput only |
| `KERNEL_BW` | 0.15 rad | `GR_KERNEL_BW` | CHOSEN | joint-space kernel bandwidth for edge weights and for seeding |
| `GEO_SCALE` | 0.5303 | `ONF_GEO_SCALE` | DERIVED | calibrated so median `f` over LODO node distances matches the trained field's median `f` on the same graph; read **only** under `ONF_CLEANLINESS=geo` |
`SUITE_HDF5_DIRS` (`src/onf/graph/build/from_demos.py`) maps suite names to HDF5 directories:
`object → libero_object`, `spatial → libero_spatial`, `goal → libero_goal`,
**`long → libero_10`** (not `libero_long`). CHOSEN — it is the upstream LIBERO layout.
---
## 6.2 Retrieval head
### Network and query shape (frozen, `schema.py`)
| constant | value | env | C/D | what breaks if it changes |
|---|---|---|---|---|
| `HIDDEN` | 64 | `GR_HIDDEN` | CHOSEN | every weight tensor; `g_head.npz` no longer loads |
| `LAYERS` | 3 | `GR_LAYERS` | CHOSEN | message-passing depth; weights are shared across layers, so depth is stored as a 0-d scalar in the checkpoint, not implied by shapes |
| `AGG` | `"sum"` | `GR_AGG` | CHOSEN | `"logsumexp"` with `m = −cost` recovers DTW's soft-min |
| `AGG_TEMP` | 1.0 | — | -- | logsumexp temperature; inert under `agg="sum"` |
| `HIST_H` | 8 | `GR_HIST` | CHOSEN | query window `W`; equals one policy action chunk |
| `SEG_K` | 8 | `GR_SEG_K` | CHOSEN | reference-segment length; a point target restores `q` but not `qdot` |
| `SEED_TOPK` | 256 | `GR_SEED_TOPK` | CHOSEN | nodes given a nonzero `h0`; everything else stays exactly 0 |
| `SEED_VEL_W` | 2.0 | `GR_SEED_VEL_W` | CHOSEN | exponent on the velocity-direction factor in seeding; 0 disables it |
| `ADVANCE` | 0 raw frames | — | DERIVED | the head answers "where am I now"; the transition kernel owns how far forward, and its fitted mixture already advances E[a] = 9.6 raw frames per 8-step check |
### Objective weights (frozen, `schema.py`)
| constant | value | env | C/D | what breaks if it changes |
|---|---|---|---|---|
| `PHASE_CE_W` | 1.0 | `GR_PHASE_CE_W` | CHOSEN | primary term: CE on the 20-bin phase marginal |
| `PHASE_EXPECT_W` | 0.5 | `GR_PHASE_EXPECT_W` | CHOSEN | primary term: phase-expectation regression |
| `NODE_W` | 0.1 | `GR_NODE_W` | CHOSEN | auxiliary node-identity BCE+rank; strand identity is only 8–15% learnable |
| `PHASE_BIN_SMOOTH_W` | 1.0 | — | CHOSEN | ±1-bin smoothing mass on the phase-CE target |
| `PHASE_BIN_SMOOTH_T` | 1.0 | — | CHOSEN | smoothing decay `exp(−|db|/t)` |
| `WHERE_PHASE_BAND` | 0.05 | `GR_WHERE_PHASE_BAND` | CHOSEN | multi-positive candidate band; one `NBINS_ALIGN` bin, same width as the deployed entry band |
| `WHERE_MOVE_TEMP` | 0.1 | `GR_WHERE_MOVE_TEMP` | CHOSEN | softmax temperature on inverse movement cost in the soft target |
| `WHERE_TRUE_BONUS` | 2.0 | — | CHOSEN | multiplicative boost on the true continuation |
### Curriculum (frozen, `src/onf/graph/train/data.py`)
| constant | value | C/D | what breaks if it changes |
|---|---|---|---|
| `N_NEG` | 16 | CHOSEN | mined hard negatives per query, across buckets (a)/(b)/(c) |
| `SELFX_K` | 16 | CHOSEN | local kNN pool searched for the self-intersection bucket |
| `SELFX_PHASE_GAP` | 0.15 | CHOSEN | `|Δphase|` that promotes a near-in-`q` neighbour into bucket (b) |
| `ENTRY_FRAC` | 1/3 | CHOSEN | share of the **non-static** queries drawn as ENTRY |
| `ENTRY_LO_MULT` / `ENTRY_HI_MULT` | 0.25 / 50.0 | DERIVED | radius range is `(0.25·p50, 50·p99)` of the measured LODO demo-start noise floor |
| `ENTRY_LOGUNIFORM` | `True` | DERIVED | the range spans two decades; a linear draw puts ~90% of mass in the top decade |
| `ENTRY_STATIC_FRAC` | 0.15 | CHOSEN | drawn **first**, before the ENTRY coin flip, so it is a share of all queries; without it the deployed t=0 window shape never appears in training |
| `ENTRY_STATIC_BAND` | 0.05 | CHOSEN | phase band of the static class's soft-target pool; matches `GR_ENTRY_BAND` exactly |
| `ENTRY_STATIC_NODE_W` | 1.0 | CHOSEN | replaces `node_w` on static rows (their phase terms are trivial — the whole pool is in bin 0) |
| `ENTRY_STATIC_SRC_AUX_W` | 0.0 | CHOSEN | the source-node warm-up would fight the uniform-over-pool target |
### Loss terms (frozen, `src/onf/graph/train/loss.py`)
| constant | value | C/D | what breaks if it changes |
|---|---|---|---|
| `ALPHA_BCE` | 0.3 | CHOSEN | node term is `0.3·BCE + 0.7·RANK`, the whole of which `NODE_W` then demotes |
| `SRC_AUX_W` | 0.05 | CHOSEN | weight of the kNN-solvable "predict the source node" warm-up |
| `ABSTAIN_W` | 0.5 | CHOSEN | weight of the abstain decision term |
| `ABSTAIN_MARGIN` | 1.0 nats | CHOSEN | margin on the exact statistic inference reads, `abstain_logit − logits[reached].max()` |
| `ABSTAIN_NODE_MAX_FALLBACK` | 0.0 nats | CHOSEN | keeps the margin defined when nothing is reachable |
| `REL_INIT_STD` | 0.02 | CHOSEN | relation-embedding init (`net/modules.py`) |
| `LOG_W_EPS` | 1e-12 | CHOSEN | floor inside `softmax(log w)` pooling |
### Training loop (frozen, `src/onf/graph/train/loop.py`)
| constant | value | env | C/D | what breaks if it changes |
|---|---|---|---|---|
| `N_QUERY` | 4096 | — | CHOSEN | queries generated per stage |
| `N_EVAL` | 128 | — | CHOSEN | held-out queries for the per-epoch report |
| `HELDOUT_STRIDE` | 5 | — | CHOSEN | split is by **demo strand**, never by frame — adjacent frames leak |
| `LR` / `WEIGHT_DECAY` | 1e-3 / 1e-5 | — | CHOSEN | AdamW |
| `GUARD_MRR_TOL` | 0.01 | — | CHOSEN | admissibility gate; an epoch that quietly degrades clean retrieval cannot become "best" |
| `stages` | `(1,)` | — | CHOSEN | stages 2 and 3 were deleted; any other value raises |
| `qbatch` | 8 | `GR_QBATCH` | -- | queries per forward/backward; pure throughput — the loss returns a batch **mean**, so the per-query learning rate is unaffected |
### Readout, at retrieval time
| knob | default | env | C/D | what breaks if it changes |
|---|---|---|---|---|
| `READOUT_ARMS` | `("euc_raw", "basin")` | — | CHOSEN | the registry; `euc_raw` is the t=0 arm, `basin` the t>0 arm |
| `GraphConfig.readout` | `"euc_raw"` | `GR_READOUT` | CHOSEN | which arm consumes `p(v|Q)` |
| `GraphConfig.temp` | 1.0 | `GR_TEMP` | -- | readout softmax temperature |
| `GraphConfig.topm` | 32 | `GR_TOPM` | -- | candidate nodes kept for readout; ENTRY widens it to the whole surviving pool so the mask is not re-truncated |
| `GraphConfig.seed_decay` | 0.9 | `GR_SEED_DECAY` | CHOSEN | temporal decay `0.9^k` over the query window during seeding |
| `GraphConfig.idf` | 1.0 | `GR_IDF` | -- | exponent on the additive `log_idf` prior (GFM-RAG eq. 15–16) |
| `GraphConfig.move_cost` | `True` | `GR_MOVE_COST` | CHOSEN | passes the movement-cost channel through at deploy so train and deploy see the same input |
| `GraphConfig.entry_band` | 0.0 (off) | `GR_ENTRY_BAND` | CHOSEN | masks candidates to `phase ≤ band` |
| `GraphConfig.task_prior_w` | 0.0 (off) | `GR_TASK_PRIOR_W` | CHOSEN | nats of additive task log-prior the belief filter's own task marginal contributes when the text lane resolved nothing; 1.0 is the plain Bayesian value. Consulted only in the no-lane case, so the six axes whose lane resolves are bit-unchanged |
| `GraphConfig.device` | `""` = auto | `GR_DEVICE` | CHOSEN | CPU and CUDA disagree by up to ~1e-4 rad elementwise |
| `GraphConfig.graph_dir` | `""` | `GR_GRAPH_DIR` | — | overrides `Paths.graph()` |
---
## 6.3 Sentinel (t > 0)
### Frozen in `schema.py`
| constant | value | env | C/D | what breaks if it changes |
|---|---|---|---|---|
| `TRACK_ADVANCE_SET` | (0,1,2,4,6,8,12,16) | — | DERIVED (upper end) | raw-frame advances mixed by the kernel. `0` is mandatory and validated — it is the stall self-loop. 16 ≈ 2× the nominal check spacing, because rollouts run longer and slower than the median demo |
| `TRACK_KERNEL_RELATIONS` | `next1..next16` + `sibling` | — | CHOSEN | excluding `align` and every `prev^d` is the asymmetry that suppresses the measured 8% backwards aliasing |
| `TRACK_BELIEF_TOPK` | 2048 | — | -- | belief entries kept when a readout is handed to host memory |
| `BASIN_RADIUS_QUANTILE` | 0.95 | — | DERIVED | p95 of LODO NN distances → the certified radius `r` |
| `BASIN_BANDWIDTH_QUANTILE` | 0.50 | — | DERIVED | p50 of the same → the KDE bandwidth / stop-short margin `h` |
| `BASIN_MIN_DEMOS` | 2 | — | CHOSEN | cells with fewer distinct demos inherit the per-task median `r`/`h` |
`r_eff = max(r − h, 0)` is DERIVED. `π`, `β` and `leak` are DERIVED — fitted by
`onf.graph.build.tracker_fit` and stored in `g_track.npz`, not in any config.
### `SentinelConfig`, `SN_*` (env)
| field | default | env | C/D | what breaks if it changes |
|---|---|---|---|---|
| `check_every` | 8 | `SN_CHUNK` | DERIVED | `_POLICY_CHUNK = 8`. A value that does not divide the chunk is **refused**: the client executes a cached chunk open-loop, so a check landing mid-chunk cannot influence the plan already in flight |
| `graph_win` | 8 | `SN_GRAPH_WIN` | DERIVED | must equal `HIST_H` |
| `graph_stride` | 0 → 1 | `SN_GRAPH_STRIDE` | DERIVED | `_TRAIN_WINDOW_STRIDE = 1`. Training windows are 8 **consecutive** raw frames (`finite_diff_vel` is a 1-frame backward difference); stride 8 would hand the net ~8× the velocity magnitude it ever saw. Any other value is refused |
| `graph_topm` | 32 | `SN_GRAPH_TOPM` | -- | candidate nodes kept before readout |
| `graph_temp` | 1.0 | `SN_GRAPH_TEMP` | -- | readout softmax temperature |
| `blend_alpha` | 0.0 | `SN_BLEND_ALPHA` | CHOSEN | weight on the tracking chunk. 0.0 is bit-exactly the frozen policy — `BlendPlan.apply` returns the chunk untouched — so a `SENTINEL=1` run at the default reproduces base numbers exactly. Unread under `blend_learned` |
| `blend_learned` | `False` | `SN_BLEND_LEARNED` | CHOSEN | read the per-row, per-block weight off the trained `AlphaNet` in `g_alpha.npz` instead of using `blend_alpha`. A missing file **raises**: a run that silently measures a different mechanism than the one it was asked for is worse than a run that does not start |
| `blend_scale` | 1.0 | `SN_BLEND_SCALE` | DERIVED | multiplier on the LEARNED weight, unread unless `blend_learned`. The chunk-MSE objective fits the right per-check shape at too confident a level, so the head is rescaled onto the average authority a fixed weight was measured at. **Derived per head, never copied:** the deployed 0.27 is `0.31 × 0.372/0.425`, matching the base-frame head's deployed mean to the previous head's — [chapter 03 §3.6](03-action-chunk-blend.md) |
| `blend_bound` | 0.0 | `SN_BLEND_BOUND` | DERIVED | action-unit saturation of `a_track`'s row 0, `bound·tanh(row0/bound)`; 0 disables it, which is what the measured fixed-alpha cells ran with. 1.0 is the value the simulator's own `[-1, 1]` action clip implies. Under `blend_learned` this is unread and the bound is `ACTION_LIMIT` — [chapter 03 §3.5](03-action-chunk-blend.md) |
| `action_scale_path` | `""` | `SN_ACTION_SCALE` | — | metres/radians per action unit. `""` resolves to `action_scale.json` beside the graph artifacts; a missing file **raises**, naming `scripts/fit_action_scale.py`. There is no default calibration: a wrong scale mis-drives the arm silently |
| `allow_stride_mismatch` | `False` | `SN_ALLOW_STRIDE_MISMATCH` | CHOSEN | opt-out of the stride guard. A **field**, not a bare env read inside the guard, so `asdict(cfg)` records that a run disabled a safety check |
| `allow_misaligned` | `False` | `SN_ALLOW_MISALIGNED` | CHOSEN | opt-out of the chunk-alignment guard, same reasoning |
| `graph_device` | `""` = auto | `SN_GRAPH_DEVICE` | CHOSEN | |
| `graph_dir` | `""` | `SN_GRAPH_DIR` | — | overrides `Paths.graph()` |
### `scripts/build_sentinel_artifacts.py` (frozen, CLI-overridable)
| constant | value | CLI flag | C/D | what breaks if it changes |
|---|---|---|---|---|
| `HELD_OUT_STRIDE` | 5 | `--held-out-stride` | CHOSEN | `owner % 5 == 0` is held out (100/500 on `long`); the kernel fit set shrinks or grows |
| `FIT_CLEAN_STRIDE` | 3 | `--fit-clean-stride` | CHOSEN | wall-clock bound on the clean half of the fit set |
| `N_DRIFT_SEQ` / `N_CROSS_STRAND_SEQ` | 80 / 80 | `--n-drift` / `--n-cross-strand` | CHOSEN | the two regimes that make `β` identifiable; on clean-only sequences `β = 0` is correct and the fit is uninformative |
| `DRIFT_SEQ_SEED` | 0 | — | CHOSEN | reproducibility of the sampled sequences |
| `N_PROBE_DEMOS` | 40 | — | CHOSEN | sanity-probe sample size (debug only) |
| `BURNIN_CHECKS_MIN` | 3 | — | CHOSEN | probe burn-in |
| `PROBE_PHASE_FRAC` | 0.5 | — | CHOSEN | probe mid-episode |
---
## 6.4 Action-chunk blend
The runtime knobs are `SentinelConfig` fields, listed in §6.3 — the blend is issued by the sentinel's
`plan()`. What follows is everything else the mechanism is parameterised by. Full reasoning:
[chapter 03](03-action-chunk-blend.md).
### Frozen in `src/onf/blend/ee_track.py` and `src/onf/blend/alpha.py`
| constant | value | C/D | what breaks if it changes |
|---|---|---|---|
| `ACTION_DIM` / `POSE_DIM` / `GRIP_COL` | 7 / 6 / 6 | CHOSEN | the OSC action layout. `GRIP_COL` is copied through, never mixed: the command is binary |
| `DEFAULT_MIN_R2` | 0.8 | CHOSEN | `ActionScale.check`'s bar. A rotation block below it usually means the delta was subtracted rather than composed in the world frame |
| `_SMALL_ANGLE` | 1e-3 rad | CHOSEN | the Taylor branch of the Rodrigues trig quotients; under autograd it also keeps the gradient finite |
| `ACTION_LIMIT` | 1.0 | DERIVED | row-0 saturation amplitude, applied to the tracking chunk and to the regression target alike. LIBERO clips the executed action to `[-1, 1]` and real demo actions max at 0.938. A constant, not a parameter: the one run that learned it drove it to 24.5 |
| `HIDDEN` (AlphaNet) | 64 | CHOSEN | width of the one hidden layer. Stored in `g_alpha.npz`, so changing it does not break loading old heads |
| `N_BLOCK` | 2 | CHOSEN | weights per chunk row: one for position, one for rotation. 1 would re-merge the two blocks |
| `N_SCALAR` | 3 | DERIVED | off-manifold distance, top-1 posterior mass, posterior entropy |
| `EXPECT_K` | 32 | DERIVED | `expected_ee_segment`'s support width; matches `ReadoutContext.topm` |
The two fitted scales are not constants — `ActionScale` is read from `action_scale.json`. Measured
values are in [chapter 03 §3.4](03-action-chunk-blend.md).
### Weight training (frozen, `src/onf/graph/train/corrupt.py`, `align.py`, `loss.py`)
| constant | value | C/D | what breaks if it changes |
|---|---|---|---|
| `p_wrong_task` / `p_offset` / `p_wrong_phase` | 0.10 each | CHOSEN | share of training rows given a deliberately wrong retrieval. At 0 the alpha gradient is one-sided and alpha can only learn arm displacement |
| `phase_tol` | 0.05 | CHOSEN | `WRONG_TASK`'s node must be this close in phase, so the failure is a genuine alias |
| `phase_min` | 0.15 | CHOSEN | `WRONG_PHASE`'s node must be at least this far |
| `offset_m` | (0.05, 0.25) m | CHOSEN | `OFFSET`'s displacement magnitude: an object's own width to a shelf away |
| `ALIGN_H` | 0.05 m | CHOSEN | mean warped DTW distance below which two segments count as the same path (~a gripper width) |
| `CHUNK_PHASE_CE_W` | 0.2 | CHOSEN | the CHUNK objective's anti-collapse regulariser |
| `CHUNK_ALIGN_W` | 0.1 | CHOSEN | weight of the DTW alignment teacher, against an MSE term of 1.0 |
---
## 6.5 Paths and process environment
`src/onf/config.py::Paths`. Resolution order is: explicit argument, then the variable, then
repo-local.
| root / method | fallback | env |
|---|---|---|
| `Paths.data` | `<repo>/data` | `ONF_DATA` |
| `Paths.results` | `<repo>/results` | `ONF_RESULTS` |
| `Paths.outputs(*parts)` | `<repo>/outputs` | `ONF_OUTPUTS` |
| `Paths.fwm(suite)` | `data/fwm/` for `object`/`default`, else the `fwm_dir` from `configs/suites.yaml`, else `data/fwm/<suite>` | `QNDF_DIR` (wins outright) |
| `Paths.graph(suite)` | `outputs/<suite>/latest/artifacts` | `GR_GRAPH_DIR` (wins outright) |
| `Paths.hdf5(name)` | first existing of `data/libero_hdf5/<name>`, `data/libero_datasets/<name>` | — |
`_HDF5_ROOTS = ("libero_hdf5", "libero_datasets")` is CHOSEN — a historical two-batch split that
`configs/suites.yaml` does not record.
### The three env reads that live outside `config.py`
Deliberate: none of them is a run knob these dataclasses carry.
| variable | default | read in | effect |
|---|---|---|---|
| `ONF_CLEANLINESS` | `field` | `graph/core/geometry.py::CleanlinessConfig.from_env` | `geo` swaps the trained field for the weight-free `GeometricField` |
| `ONF_GEO_SCALE` | `GEO_SCALE` = 0.5303 | same | distance scale of that stand-in |
| `SN_GRAPH_TRACE_DIR` | unset | `sentinel/sentinel.py` | per-episode `.npz` debug trace sink; read once at construction, never on the control path |
### Mandatory for every run
```bash
export PYTHONPATH=src OMP_NUM_THREADS=4
```
---
## 6.6 Modes
`evals/common/modes.sh::onf_set_mode` is the **only** definition of a mode.
`scripts/run_sr.py::parse_mode_recipes` parses this file rather than re-declaring the recipes, so
every arm must stay shaped as `name) export VAR=val ... ;;` — no control flow inside an arm, one
`export` per arm (backslash continuations are fine).
| mode | exports | what it is for |
|---|---|---|
| `base` | nothing | frozen policy, no intervention. A reporting baseline, not an ablation |
| `blend` | `SENTINEL=1`, `SN_BLEND_ALPHA=0.0` | the plumbing check. Bit-identical to `base`; a cell that differs here is a bug, not a result |
| `blend_a05` | `SENTINEL=1`, `SN_BLEND_ALPHA=0.5`, `GR_GRAPH_DIR` at the ADVANCE=0 head | the first fixed-alpha probe ([`../results.md`](../results.md) §5, superseded — but still the best arm on Camera_Viewpoints) |
| `blend_a015` | as above at `0.15` | how much of `blend_a05`'s loss is intervention *magnitude* rather than a wrong blend direction |
| `blend_bounded` | as above at `0.3`, plus `SN_BLEND_BOUND=1.0` | everything the blend gained except the learned weight — the row-0 bound and the task-lane fix — held at a fixed alpha so a gap left here is one alpha has to close |
| `blend_a015_bounded` | as above at `0.15` | the best FIXED weight measured: 262/312 = 84.0% on Objects_Layout, the bar the learned weight has to clear |
| `blend_scaled015` | `SENTINEL=1`, `SN_BLEND_LEARNED=1`, `SN_BLEND_SCALE=0.31`, `GR_GRAPH_DIR` at the v3 Stage-2 artifacts | the learned weight's *shape* at the authority a fixed weight was measured at — 88.1% on Objects_Layout |
| `blend_full` | as above with `SN_BLEND_SCALE=0.27`, `GR_TASK_PRIOR_W=1.0` and the base-frame v4 head | **the current recipe.** 7-axis total +7.9 pp ([`../results.md`](../results.md) §6) |
`evals/common/modes.sh` carries a dozen further probe arms with the measurement that motivated each
in its comment; the table above is the ladder, not the inventory.
The fixed-alpha blend arms pin `GR_GRAPH_DIR` at the ADVANCE=0 head on purpose: under the old
`+4` head the feedforward rows of `a_track` aim ~13.6 frames ahead on an 8-frame chunk, which a
position-restoring servo absorbed and a feedforward blend does not
([chapter 02 §2.4](02-retrieval-head.md), [chapter 03](03-action-chunk-blend.md)).
---
## 6.7 Easy to get wrong
1. **Assigning a literal to a flat `schema.py` name.** The flat names are aliases derived from
`DEFAULTS`; a literal creates a second source of truth that can drift from the dataclass the
validation lives on.
2. **Changing a §6.1 constant and reusing an old head.** Anything in that table renumbers nodes or
re-labels edges, so `graph_hash` changes and every stamped artifact must be rebuilt. The loader
will refuse — see [chapter 05 §5.2](05-artifacts.md#52-graph_hash--the-most-important-check-in-the-subsystem).
3. **Setting `SN_GRAPH_STRIDE` from `SN_CHUNK`.** They are independent. `check_every` is how often
you *check*; `graph_stride` is the spacing *within* the window. Setting stride to 8 gives the
network ~8× the velocity it trained on. The guard refuses it; `SN_ALLOW_STRIDE_MISMATCH=1`
silences the guard, not the problem.
4. **Exporting a `SN_CHUNK` that does not divide 8.** Refused for the same class of reason: a check
landing mid-chunk cannot influence the chunk already in flight.
5. **Adding a tuning knob as a bare `os.environ.get`.** A knob that is not a field on one of the
two dataclasses does not appear in the run's `config.json`, so the run record cannot say what it
was set to. The two `SN_ALLOW_*` opt-outs are fields precisely for this reason.
6. **Running a blend mode without checking which `action_scale.json` it resolved.** The blend
converts metres and radians into action units; the calibration carries no `graph_hash` and no
suite stamp, so a file fitted on another suite loads without complaint and rescales every action
the arm executes.
7. **Setting `SN_BLEND_ALPHA` or `SN_BLEND_BOUND` under `blend_learned`.** Both are unread there:
the trained head owns the weight and `ACTION_LIMIT` owns the bound. A run that appears to set a
knob it does not set is worse than one that sets nothing.
---
**Next:** [`../results.md`](../results.md) — what these settings measure, or back to the
[README](../../README.md).

Xet Storage Details

Size:
24.2 kB
·
Xet hash:
33d1809e7ba28d19e1c11e9bff5ceb66ee30d5177d629fe55feddb8d455fe9cf

Xet efficiently stores files, intelligently splitting them into unique chunks and accelerating uploads and downloads. More info.