docs: correct iteration, backward, skip, edit-attach and generate claims - #718
Merged
Merged
Conversation
… edits and generate Nine verified findings from the 0.8 stress sweep, all documentation: - gotchas/iteration.md: a backwards read inside a loop starting above step 0 binds to the next step instead of raising, and the "never reached" warning covers that case too. - usage/iter-all-next.md: loops do not nest cleanly — a read after an inner loop binds to the step that loop left the model on. - usage/backward-and-grad.md: a backward block cannot nest inside another; an intervention inside a gradient-checkpointed segment never reaches the backward. - models/vllm.md, usage/skip.md: a skip has to cover the whole step's rows, which under continuous batching are not all yours, and the failure kills the engine; a vLLM decoder layer's .input is the positions tensor, not the hidden state. - patterns/attribution-patching.md: per-head attribution off attn.output[0] is wrong after the projection — use its input or the attention source op. - usage/edit.md, patterns/sae-and-auxiliary-modules.md, usage/cache.md: attach a module as a replacement, not an in-place write, or backward through it fails. - usage/generate.md, CLAUDE.md: generate follows the checkpoint's generation_config, which on many instruct checkpoints samples. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
`generate` decodes with the checkpoint's `generation_config`, not greedily, in the API table, the pipe comparison and the 0.8 version note; an attachment is routed into a layer as a replacement, not an in-place write, in the integrations gotcha. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…f it Batching gained the modulo rule and the unscoped-write warnings in #722; the concept page still described the old exact-match rule. State the rule the batcher applies, what a value it cannot scope does to a read and to each kind of write, and keep the shape coincidence as the accepted cost it now is. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Documentation only — no code changes. Nine findings from the 0.8 agent stress sweep, each one a page promising something the implementation does not do. Every claim below was re-measured in this worktree unless noted.
What was wrong, and what changed
tracer.iteroff-by-one (item 9). Inside a loop starting above step 0 the step pin relaxes after the body's first request, so a read that goes backwards in model order silently binds to the next occurrence instead of raisingOutOfOrderErroras the same body does in a plain trace or underiter[0:...]. Measured on gpt2: baselineh[0]norms[54.691, 61.350, 53.869, 54.052], anditer[1:3]readingh[6]thenh[0]returns[53.869, 54.052]— labels 1 and 2 holding steps 2 and 3. When the skew runs off the end of the run it surfaces as the "never reached" warning, which reads as "the loop asked for a step the run did not make" and points atmin_new_tokens=; a descending step list (iter[[3, 1]]) is cut short the same way although the run made step 1. New section indocs/gotchas/iteration.md, both snippets run.docs/usage/iter-all-next.md's "Whateveriterationwas before the loop is restored on exit, so loops can nest" is the same mechanism read backwards: what is restored is a relaxed pointer, so a read after a nested inner loop binds to the step the inner loop left the model on. Measured: an inneriter[[3]]inside an outeriter[1:2]makes the outer read return step 3's value. Sentence corrected and linked to the gotcha.Gradient checkpointing (item 10). One gotcha line in
docs/usage/backward-and-grad.md:torch.utils.checkpointruns the segment twice, the block served the first pass, and the recompute — the one autograd differentiates — runs unmodified, so the forward output is right and the parameter gradients inside the segment are wrong with nothing to signal it. Turn checkpointing off while tracing.Nested backward blocks (item 17). One gotcha line: a backward block inside another dies with
ValueError: cyclic parent chainfrom inside autograd. Re-ran the repro here — sibling blocks work (create_graph=True, retain_graph=Trueon the first), both the model case and the plain-tensor case raise..skip()on vLLM (item 15). New section indocs/models/vllm.mdplus a Limitations bullet: a skip has to tile every row of the step, and a step's rows hold other traces', other tenants', and decodes whose block already finished, so with more than one request in flight the skip cannot cover it; theValueErroris raised outside the per-request deferral, so it takes the engine down (EngineDeadError, every client's in-flight requests lost) rather than the request. Whether a script survives is the scheduler's choice.docs/usage/skip.mdgains the same limitation and the reason its pass-through idiom kills a vLLM engine: a decoder layer is calledforward(positions, hidden_states, residual), so.inputis the positions vector — readargs, kwargs = layer.inputsinstead. Not re-run here (no vLLM engine to spare on a shared card); taken from the sweep's six logged reproductions and the source.Per-head attribution (item 23.1).
attribution-patching.mdtold you to reshapeattn.output[0], which is the output ofc_proj, where the hidden dimension no longer decomposes per head — its own sibling page refutes it in a warning box. Replaced with the projection input (attn.c_proj.input/o_proj.input) or.source.attention_interface_1.output[0]. Verified on gpt2 with the page's own prompt pair: the two correct routes agree bitwise (max abs diff 0.0), and the old route disagrees in sign for 70 of 144 heads and names a different top head,(9, 8)against(6, 9).In-place module attach (item 23.2).
layer.output[:] = layer.adapter(acts, hook=True)writes the attachment's output into the tensor that is its own input, so the backward the pattern exists for raisesRuntimeError: one of the variables needed for gradient computation has been modified by an inplace operation. Confirmed by runningrepro_4_inplace_backward.py: in-place fails, the replacement form (layer.output = ...) backprops. Fixed atedit.md:94/:103andsae-and-auxiliary-modules.md:358, and at the same idiom incache.md:225andgotchas/integrations.md:97— the latter re-run here on the non-inplacemodel.edit()path it documents: the attachment's internals stay observable andadapter.inner.weight.gradarrives."Greedy by default" (item 23.3). False: generate uses the checkpoint's
generation_config, andgemma-3-1b-it,Llama-3.1-8B-InstructandQwen3-4B-Instruct-2507all shipdo_sample=Truein it. Ran gemma-3-1b-it: two default generates differ,do_sample=Falsemakes them identical. Fixed ingenerate.md(front matter, intro, the section, the comparison table),CLAUDE.md, and the three pages that repeat the claim:reference/api-quick-reference.md,usage/pipe.md's comparison table andreference/version-history.md.cache(include_inputs=True)(item 23.5). Documented as behaviour, not promised as a fix: thedevice=move reaches tensors and containers, not tensors inside arbitrary objects, so a decoder block'spast_key_valueskeeps the model's liveDynamicCacheon the compute device. Re-measured on gpt2, batch 32 x ~80 tokens, 8 traces held: 8 MiB of GPU growth without it, 1320 MiB with it, about a fifth more time per trace; the memory returns when the caches are released. The gotcha bullet claiming the cache moves tensors to CPU is qualified.Batching row rule (follow-on to #722).
docs/concepts/batching-and-invokers.mdstill stated the old exact-match rule in three places (the intro, the narrow/widen bullets, the gotcha). Rewritten againstsrc/nnsight/intervention/batching.pyonfix/batching: a tensor is scoped when its leading dim istotalor a whole multiple of it (k = shape[0] // total, narrowed at[start*k, size*k)), anything else is served whole, a replacement for such a value is dropped with a warning and an in-place edit to one is caught by torch's version counter at the next narrow/widen. The page's acknowledgement that a shape coincidence can mislead now reads as the accepted cost of the modulo rule. Verified by running that branch's ownTestFlattenedRowsandTestUnscopedWrites(6 passed) from agit archiveoffix/batchingin a scratch directory — no other worktree touched, no stash.Tested
No code changed, so no test run applies. Every snippet I wrote or corrected was executed against this worktree's
src(PYTHONPATH=<worktree>/src) on gpt2 or gemma-3-1b-it, except the vLLM ones — I did not build an engine on the shared GPU, and those rest on the sweep's reproductions plus the source.Deliberately left alone
docs/models/transformers-model.md:115and:347, and the in-place attach idiom atdocs/concepts/envoy.md:157. Every other occurrence is fixed here.docs/models/vllm.md:99's "output[1]the residual stream entering it" is measurably the wrong residual per the sweep, but it belongs to skill item 24 and I left the wording untouched.docs/concepts/envoy.md) is another agent's.🤖 Generated with Claude Code