Buckets:
Concepts
The idea, end to end, without the code. Read this before docs/technical/.
No measured number appears in this file. Every success rate this repo has produced lives in
results.md, and nowhere else.
1. The problem
A vision-language-action policy is frozen. It was trained on demonstrations and it is not going to be retrained, finetuned, LoRA'd or adapted. Perturb the world and it drifts: the rollout leaves the demonstrated manifold — a missed grasp, an accumulated tracking error, a distractor that pulled the arm off course.
That is a monitoring problem. Something has to keep watching and, at every action chunk, say where on the demonstrated support the arm should be.
2. One model
Build a graph out of the training demonstrations. Nodes are short runs of consecutive frames; edges connect them forward in time, sideways across demonstrations of the same task, and across tasks at the same point in the episode.
Train one small network that takes a window of recent joint states and returns a distribution over the graph's nodes: given what the arm has been doing, which demonstrated moment does this look like?
That distribution is the whole method. It is filtered over time: each new observation updates a belief over nodes, and the belief — not the raw observation — is what says where the arm should be.
Nothing about this is policy-specific. The graph and its head are trained once per suite from demonstration data alone. The same artifacts drive a frozen StableVLA and a frozen GR00T-N1.7 with no retraining of either.
3. Why a graph rather than a nearest neighbour
The obvious baseline is: store every demo frame, find the closest one to the current joint configuration, go there.
That fails for a reason worth stating precisely. A manipulation trajectory revisits the same joint configurations at different times — the arm passes through nearly the same pose reaching for an object and retracting from it. Position alone does not identify when you are. A nearest neighbour returns a confident answer to the wrong question.
The graph's edges encode what nearest-neighbour lookup throws away: what comes next. The training task is built around that. The network is never asked "which node is this window closest to" — it is asked "which node comes a few frames after this window", which cannot be answered by a distance computation.
4. Why this needs a filter
The retrieval head is memoryless. Every call answers from scratch. On a suite where each task involves two objects, reaching for the second is kinematically close to reaching for the first, and a memoryless matcher aliases them — it will happily report that the arm has jumped backward to an earlier phase, or teleported forward past work it has not done.
A belief filter fixes this cheaply. Carry a distribution over nodes across checks; push it forward through a transition model built from the graph's own forward edges; multiply by the new observation. A backward jump now has to pay transition cost, because the transition model has no backward edges. Aliasing that the observation cannot resolve gets resolved by dynamics.
5. Deciding where the arm should be
The answer is a projection, not a point. The basin around a node is a certified region fitted from held-out demonstrations. If the arm is already inside it, the correction is exactly a no-op. If not, the target is the boundary of the region — undo the drift, do not overshoot into a pose the policy has no reason to expect.
The reading is a short segment, not a single configuration. A point target restores position while
leaving velocity wrong, which hands the policy a state that is in-distribution in q and
out-of-distribution in qdot — the precise failure the correction exists to prevent.
The belief is read one check AHEAD: the target comes off the belief pushed through the transition kernel, not off the belief itself, so it keeps the direction of travel rather than only the position.
6. Applying the reading
A reading is worth nothing until something acts on it, and there are two ways to act. Hand the reading to a second controller that drives the arm towards it, or fold it into what the policy was already going to do. This repo did the first and now does the second.
The first way is a servo. It works — it restores position — but it is a second authority over the same arm, so everything about it is a handover problem: when to take control, when to give it back, how hard to pull, and what happens to the half-finished motion the policy had in flight. Every one of those questions needs its own mechanism, and every mechanism is another thing that can fire when it should not. That whole stack has been deleted.
The second way is a blend. The reference segment is converted into the policy's own action units and mixed into the action chunk the policy has just produced, at one point, once per chunk. No handover exists to get wrong, because the policy never stops driving. What replaces the handover question is a weight: how much of the mixed-in chunk to take.
That weight is the whole difficulty, and it is not a tuning constant. Steering towards a demonstration helps when the demonstration is still the right one and the policy is merely mis-seeing the scene; it actively hurts when the demonstration has expired — the object has moved, or the instruction was reworded and retrieval went to another task entirely. Following a stale demonstration confidently is worse than doing nothing. So the weight has to be a judgement about the pair: this query, and this thing that was retrieved for it. It is learned, from data that includes deliberately wrong retrievals, because clean data can only ever teach "the arm has drifted, pull harder" and never "this demonstration no longer applies".
Two smaller consequences of blending rather than servoing are worth stating, because both were bugs first. A correction issued in action space has no gain limit unless one is written down, so the tracking chunk's first row — a raw pose error — is saturated explicitly. And the gripper command is binary, so it is never mixed at all; the policy's own bit passes straight through.
7. What this method does not do
- It does not touch the policy. No gradients, no adapters, no prompt surgery. It changes the actions the policy emits, not the policy that emits them.
- It does not look at pixels. Everything runs on joint configurations and end-effector poses.
- It does not know what the objects are, where they are, or what the task means. Task identity enters only as a graph label selecting which demonstrations are relevant.
- It does not plan. There is no search, no model-predictive rollout, no cost function over futures. The only thing resembling a prediction is one step of the belief filter.
8. Where the honest gaps are
Stated here rather than buried, and expanded with numbers in results.md:
- Two components that run on every single retrieval — the
ENTRY_STATICtraining class and the cleanliness weighting — have never been ablated. - The learned weight has now been evaluated: +7.9 pp over seven axes against the frozen policy. But four axes carry all of it and three do not move, and the one axis this project started on — Robot_Initial_States — is still 33.6 pp behind the joint-space prefix that was deleted to make room for the blend. On that axis the method has recovered about a third of what deleting the servo cost.
- The learned weight's per-check shape is worth +4.2 pp over the best constant weight. Its magnitude had to be corrected by hand, because the training objective (chunk MSE) does not know that a missed intervention costs a whole episode. That correction is a calibration with a derived value, but it is still a term the loss should have contained and does not.
- No blend arm has been run on a suite other than
long, or on a policy other than StableVLA. - The gate that was supposed to decide whether to correct at all never fired in the one sweep that has been run. Every check corrected.
Next: technical/01-data-and-graph.md — demonstrations to
graph, with shapes and dtypes. Or GLOSSARY.md to look up a term.
Xet Storage Details
- Size:
- 8.34 kB
- Xet hash:
- 47193f924e7dc97848af8c1b24d0851acae4235eac820baaf3d572438de93eeb
Xet efficiently stores files, intelligently splitting them into unique chunks and accelerating uploads and downloads. More info.