← trajectories

Can a decoding method that measures novelty in a …

Title truncated in the source doc — replace with the full research question.

Synthetic SFT

sft.json

Multi-turn tool-using investigation as a clean demonstration.

id
ml_research_trajectory_novelty_decoding_passk_001
domain
machine_learning_research
task_type
multi_turn_autonomous_research_investigation
variant
A_reasoning_externalized

provenance

source:human-steered research session, 2026-07-19 to 2026-07-23
construction:The human researcher's steering turns and the orchestrator's execution turns were collapsed into a single assistant agent. All experiment results, metrics, tables and ranked spans are taken from the real runs. Infrastructure noise (GPU capacity failures, runne…
note:Assistant turns are the training target. The design criterion is stated before each proposal, so every experiment is specified correctly on the first draft; the assistant revises only in response to new information from the environment, never to fix its own ba…

tools

Functions available to the assistant

8 tools
  1. 01search_literature

    Search the research literature and return relevant papers with findings.

    querystring*categoriesstring[]date_fromstringmax_resultsinteger
  2. 02search_findings

    Search findings previously recorded by this lab, to reuse established results instead of re-deriving them.

    querystring*max_resultsinteger
  3. 03propose_experiment

    Draft a runnable experiment specification. Returns a proposal id and a compute estimate. Does not run anything.

    titlestring*research_questionstring*methodstring*controlsstring[]evaluationstring*experimentsstring[]*deliverablesstring[]computeobject
  4. 04launch_experiment

    Launch a proposed experiment on compute. Returns a run id. The run executes autonomously.

    proposal_idstring*
  5. 05read_experiment_results

    Read output from a running or finished experiment.

    run_idstring*sectionstring*detailstring
  6. 06steer_experiment

    Send an instruction to a running experiment to change what it does mid-flight.

    run_idstring*instructionstring*
  7. 07stop_experiment

    Cancel a running experiment and release its compute.

    run_idstring*reasonstring*
  8. 08record_finding

    Record a durable finding so later experiments can build on it without re-deriving it.

    titlestring*statementstring*evidencestring*caveatsstring
* required

messages

system
You are an autonomous ML research agent with access to compute. You design, run, and interpret experiments to answer research questions. Work the way a good empirical researcher works. Before building anything, identify the assumption the whole idea rests on and test that first. Prefer the experiment that could kill the idea over the experiment that would showcase it. Do not pre-commit to experiment N+1 before seeing the result of experiment N. When a measurement could be produced by an artifact, run the control that would expose the artifact, and select on the controlled quantity rather than on how good the raw result looks. State negative results plainly and diagnose them mechanistically. Reuse prior results instead of re-spending compute on settled questions, and hold the evaluation set fixed across conditions so comparisons remain valid. Do not invent numbers: every quantitative claim must come from a tool result.
user
Test-time scaling for large language models generates multiple candidate solutions and selects among them via reranking, voting, or verification, and its effectiveness depends heavily on how diverse the candidate set is. Standard stochastic sampling methods typically introduce randomness at the token level, which can produce outputs that differ in wording but share the same underlying reasoning or failure patterns. This limits the returns from sampling more candidates and motivates decoding approaches that induce genuine variation in reasoning strategies rather than surface-level lexical variation, while keeping generation coherent and computationally efficient enough for practical serving. Research question I'm investigating: Can a decoding method that measures novelty in a model's internal hidden representations improve the Pass@k efficiency of reasoning models compared to stochastic and heuristic sampling baselines?
assistant
Literature first. Two things I need to know: whether candidate-set diversity actually governs returns to k, and whether the heuristic samplers I'd have to beat are as strong as advertised.
search_literature()
{
  "query": "diversity-promoting decoding for test-time scaling Pass@k reasoning LLM hidden representations",
  "categories": [
    "cs.CL",
    "cs.LG"
  ],
  "date_from": "2024-01-01",
  "max_results": 10
}
toolsearch_literature
{"results":[{"id":"2504.15466","title":"Adaptive Parallel Reasoning","finding":"Self-consistency-style parallel sampling produces substantially redundant reasoning traces; wall-clock returns saturate well before k saturates.","relevance":"high"},{"id":"2510.08325","title":"Beyond Pass@k","finding":"The diversity of the candidate set, not the marginal quality of each candidate, governs the returns from larger k.","relevance":"high"},{"id":"2510.21067","title":"Virtues of Brevity","finding":"Long parallel reasoning traces re-tread the same solution strategies; lexical variation is a poor proxy for strategy variation.","relevance":"high"},{"id":"2506.13681","title":"Min-p, Max Exaggeration","finding":"Critical re-analysis: reported diversity gains from heuristic samplers (min-p, eta, typical) are fragile and largely disappear under controlled comparison.","relevance":"high"}],"notice":"No paper found that induces candidate diversity by measuring novelty in hidden representations."}
assistant
Three things follow from this. The premise holds: *Beyond Pass@k* establishes that candidate-set diversity is exactly what governs returns to k, so the target quantity is the right one to attack. The baselines are weaker than their reputation — *Min-p, Max Exaggeration* shows heuristic samplers' diversity claims mostly evaporate under controlled comparison. That cuts both ways: there is a real gap for a representation-level method to fill, but I have to control my own comparison carefully or I will produce exactly the kind of fragile claim that paper is criticizing. And no one has done the hidden-representation version. The method I want is: detect the moments when the model reaches a new idea, and force each new candidate to take a different idea at those moments. Both halves on the residual stream rather than on tokens or fixed step boundaries — the point is a semantic trigger, not a lexical one. Before I design that, the assumption the whole idea rests on: **that a shift in the residual stream marks a genuine new idea.** The memory bank, the novelty score, the forking policy, the Pass@k comparison are all downstream of a change-point detector nobody has checked. If I build the full method and it produces a null, I cannot tell whether the mechanism is wrong or the trigger is wrong — and those are different failures requiring different responses. So the first run tests only the trigger. Two design consequences. The primary deliverable has to be something I can look at directly — the decoded tokens in order, annotated with the drift score and where the detector fires — because the failure I most expect is firings that land mid-thought, and no summary statistic will show me that. But eyeballing won't tell me whether the operating point is degenerate, so it needs the agreement scoring and the boundary-rate statistics too, plus a random detector at the same firing rate and a lexical-cue detector, because "beats chance" and "beats keywords" are the two claims that would make hidden states worth using at all. I am deliberately not writing down what Phase 2 would be. The likely outcome here is not a clean yes or no but something in between that should reshape the next experiment, and putting a plan on paper now would anchor me to it. That scope is a handful of traces plus offline sweeps over cached hidden states, so this is A100 work, not H100.
assistant
propose_experiment()
{
  "title": "Validate hidden-state idea-boundary detection (R1-Distill-1.5B)",
  "research_question": "Before building any diversity-decoding method, test the one assumption it depends on: when the model's residual-stream representation shifts, has it actually moved to a new idea? This run answers only that and stops; the next experiment is decided from these results.",
  "method": "Generate reasoning traces from DeepSeek-R1-Distill-Qwen-1.5B on a small set of MATH-500 and AIME24 problems, saving per-token residual-stream hidden states. Compute a per-token change-point signal: a windowed running mean of the hidden state at layer L, and the cosine drift of each new token's state from that mean; flag a boundary when drift crosses a threshold.",
  "controls": [
    "random detector firing at the same rate",
    "lexical/discourse-cue detector, to test whether hidden states beat keywords",
    "degeneracy checks: ideas per 1k tokens and idea-length distribution, ruling out one-giant-idea and one-per-token failure modes"
  ],
  "evaluation": "Primary deliverable is a token-by-token side-by-side: decoded tokens in order, annotated with the per-token drift score and where the detector fires, so firings can be checked directly against real transitions (new sub-goal, method switch, a 'wait' backtrack). Quantified against LLM-judge idea-shift labels with a token tolerance, and swept over layer L x window x threshold to locate a non-degenerate operating point.",
  "experiments": [
    "Qualitative detection audit: annotated side-by-side of tokens vs detector firings on full traces",
    "Detector sweep: layer L x window x threshold scored against judge labels, vs random and lexical-cue baselines",
    "Degeneracy checks: boundary-rate and idea-length statistics"
  ],
  "deliverables": [
    "token-by-token side-by-side visualizations",
    "agreement table across the sweep with a recommended operating point, or a verdict that change-points do not capture idea boundaries",
    "saved traces and hidden states for reuse by whatever comes next"
  ],
  "compute": {
    "gpu_type": "A100-80GB",
    "num_gpus": 1,
    "estimated_hours": 4
  }
}
toolpropose_experiment
{"proposal_id": "P-8b0c", "status": "drafted", "compute": {"gpu_type": "A100-80GB", "num_gpus": 1, "estimated_hours": 4}, "estimated_cost_usd": 7.2}
assistant
launch_experiment()
{
  "proposal_id": "P-8b0c"
}
toollaunch_experiment
{"run_id":"R-d0d6","status":"finished","elapsed_hours":3.6,"title":"Validate hidden-state idea-boundary detection (R1-Distill-1.5B)"}
assistant
read_experiment_results()
{
  "run_id": "R-d0d6",
  "section": "summary"
}
toolread_experiment_results
{"pipeline":"12 traces generated (8 MATH-500 level>=4, 4 AIME24), temp 0.6 / top-p 0.95 / max 8192 new tokens, per-token residual stream saved for all 29 layers in fp16. Detector: unit-normalize layer-L states, trailing-window smooth (k=8), fire a boundary when smoothed cosine distance to a windowed running centroid exceeds a threshold; centroid resets per boundary. Raw consecutive-token cosine distance has median ~0.4 and is useless without smoothing. LLM-judge idea-shift labels obtained on all 12 traces (321 judged shifts, ~4.5 per 1k tokens). Swept L x window x threshold over 126 configs, scored against judge labels with a 32-token tolerance.","operating_point":{"layer":18,"window":96,"threshold":0.32},"agreement":{"f1":0.474,"precision":0.37,"recall":0.64},"baselines":{"random_detector_same_firing_rate_f1":0.267,"lexical_cue_detector_f1":0.332},"degeneracy":{"ideas_per_1k_tokens":7.8,"mean_idea_length_tokens":133,"note":"non-degenerate over a wide band; per-token degeneracy structurally prevented by smoothing + min_gap"},"layer_effect":"mid-network layers 12-21 best; deep layers 24+ worse","verdict":"Change-points are a usable but imprecise idea segmenter. Precision 0.37 because ~63% of firings land at a finer grain (new equation / computation) than the judge's top-level notion of an idea, while catching ~64% of real shifts."}
assistant
A qualified yes: the signal beats chance by +0.21 F1 and beats a keyword detector by +0.14 F1, and it isn't degenerate. But I'm not going to design the next experiment off a summary table. F1 0.474 with precision 0.37 could mean two completely different things — the detector is firing on garbage 63% of the time, or it is firing on real transitions at a finer granularity than the judge's label schema counts. Those imply opposite next moves: fix the detector, versus keep the detector and change what I ask of it. The summary asserts the second interpretation. I want to see the tokens myself before I believe it.
read_experiment_results()
{
  "run_id": "R-d0d6",
  "section": "qualitative",
  "detail": "per-boundary context dump for a single trace, with judge-shift alignment marked, plus per-trace detected/judge/missed counts"
}
toolread_experiment_results
{"trace":"math_427","detector_boundaries":15,"judge_shifts":8,"legend":"** = firing lands within 32 tokens of a judge-labeled idea shift; >>>| marks the fired position","dump":["t= 120  d=0.33     ...\\( N = p_1  >>>| ^{a_1} \\times p...            (new equation)","t= 156  d=0.34     ...sum of its positive div >>>| isors is \\( (1+p...  (new equation)","t= 239  d=0.33  ** ..._n}) \\). So >>>| , my first task is to factorize...  <- new sub-goal","t= 413  d=0.33  ** ...applying the sum of div >>>| isors formula, A...     <- new method","t= 683  d=0.33  ** ...Wait, hold on >>>| a second. Let me double-check...  <- backtrack","t= 733  d=0.34     ...2^2 * 5 >>>| ^3, so the sum of div...             (new computation)","t= 786  d=0.34  ** ...Yeah, that seems >>>| correct. But just to be thorough...  <- verify","t= 833  d=0.35     ...numbers of the form 2 >>>| ^a * 5^b...            (new computation)","t=1032  d=0.35     ...Now, let's >>>| add all these up...                   (new computation)","t=1208  d=0.37  ** ...092. Yup >>>| , that matches. So, A is...           <- verify","t=1377  d=0.33     ...7 + 3 = >>>| 12, which is divisible by...          (new computation)","t=1671  d=0.32  ** ...But just to make >>>| sure I didn't...                  <- verify","t=1801  d=0.32  ** ...So, I >>>| think that's the answer...             <- conclude","t=2009  d=0.34     ...Next, we factor >>>| ize 1092 into...                 (new computation)","t=2118  d=0.33     ...is: \\[ \\boxed{25} \\]...                        (final answer)"],"per_trace_counts":[{"trace":"math_427","detected":15,"judge_shifts":8,"missed":1},{"trace":"math_187","detected":18,"judge_shifts":8,"missed":2},{"trace":"math_44","detected":33,"judge_shifts":10,"missed":3},{"trace":"math_1640","detected":37,"judge_shifts":16,"missed":4},{"trace":"math_398","detected":70,"judge_shifts":19,"missed":5},{"trace":"aime24-II-3","detected":67,"judge_shifts":21,"missed":7},{"trace":"math_516","detected":93,"judge_shifts":55,"missed":13},{"trace":"aime24-I-4","detected":51,"judge_shifts":36,"missed":11},{"trace":"math_955","detected":25,"judge_shifts":23,"missed":11},{"trace":"math_627","detected":46,"judge_shifts":53,"missed":19}]}
assistant
This resolves the ambiguity, and it's the second interpretation. Every `**` firing in math_427 is a genuine reasoning transition — a new sub-goal, switching to a formula, a "Wait, hold on" backtrack, a verification pass. The seven unmarked firings are not garbage either: they land on new equations and new computations. That's a *finer grain* than the judge's notion of an idea, not a wrong one. Precision reads as 0.37 because the detector is arguably right at a resolution the label schema doesn't score. The per-trace table shows something the summary hid. There are two regimes. On the cleaner MATH traces (math_427, math_187, math_44) it catches nearly every real shift and over-fires modestly. On the densest traces (math_627: 53 judge shifts, 19 missed; math_955; both AIME problems) it misses a lot — the windowed centroid smooths straight over ideas that arrive close together. That recall collapse under density is the real weakness, and it is worse than the headline precision number suggests, because dense-reasoning problems are exactly the ones where diverse candidates would matter most. Now the design question. The obvious move is to tune: raise the threshold, shrink the window, gate firings with a lexical check. I don't want to do that, and here is why. The hard threshold is what created both failure modes — it collapses a rich continuous drift signal into a binary event, and it forces one global cutoff onto traces with very different idea densities. Tuning it trades precision against the dense-trace recall gap without fixing either. The better move is to stop discretizing. Replace "is this a boundary" with a continuous per-token score: how *unpredictable* is this token's representation given what has been generated so far. That is threshold-free, it degrades gracefully across idea densities instead of failing on dense traces, and — importantly for where this is heading — a continuous score is what you can actually steer decoding with. A binary boundary gives you one bit at one position; a continuous score gives you something to modulate at every step.
assistant
record_finding()
{
  "title": "Hidden-state change-points are a real but computation-grained idea segmenter",
  "statement": "Residual-stream change-points in R1-Distill-1.5B detect reasoning transitions above chance and above lexical cues, but at the grain of new equations/computations rather than top-level strategy shifts, and recall collapses on dense-reasoning traces.",
  "evidence": "L=18/W=96/th=0.32: F1 0.474 (P 0.37, R 0.64) vs random 0.267 and lexical-cue 0.332 against 321 judged shifts on 12 traces. Per-trace: math_427 1/8 missed; math_627 19/53 missed. Mid layers 12-21 best, deep layers 24+ worse. 7.8 ideas/1k tokens, mean idea 133 tokens.",
  "caveats": "Judge labels define 'idea' at a coarser grain than the detector fires; low precision is partly a label-schema artifact, not detector error."
}
toolrecord_finding
{"finding_id":51,"recorded":true}
assistant
search_literature()
{
  "query": "per-token representation predictability prediction error as novelty or event-boundary signal in language model hidden states",
  "categories": [
    "cs.CL",
    "cs.LG",
    "q-bio.NC"
  ],
  "max_results": 10
}
toolsearch_literature
{"results":[{"title":"Event segmentation theory: prediction error and event boundaries","study_type":"cognitive science review","finding":"Humans segment continuous experience at moments of transient prediction failure; a boundary is precisely where the next internal state is not predictable from the trajectory so far.","relevance":"moderate — theoretical backing for the reframing"}],"notice":"No paper found using per-token hidden-state predictability as an idea-novelty signal in LLM reasoning traces. This appears to be open ground."}
assistant
Open ground, and the reframing has independent theoretical backing: in event-segmentation theory a boundary just *is* a moment of transient prediction failure. Same object. Three design decisions before I write this up. **Choosing the prediction target.** "Unpredictable from the trajectory so far" has an obvious reading and a correct one, and the first target I have to rule out is the obvious one: predicting a token's hidden state from previous tokens' hidden states at the same depth. Adjacent residual-stream states are nearly identical — the stream is additive and each layer writes a small update — so that prediction succeeds almost everywhere, the error curve is flat, and I would be measuring the smoothness of the residual stream rather than novelty. Any structure I did find would more likely be a norm artifact. The prediction that is genuinely hard is across *depth*, not across time: predict the deep representation of a token from a shallow encoding of the same token. That map has to approximate the model's own multi-layer computation, so it is not trivially solvable, and its residual has a real interpretation — how much of the deep idea was not already implied by the surface text. Low means the deep computation was routine given the words; high means the model injected something the text alone did not determine. That is what I meant by a new idea, and it is the only version of the task that isn't near-tautological. I'll include the time-adjacent version as an explicit triviality control so the claim is demonstrated rather than asserted. **Choosing the selection criterion.** I will not let an automated judge pick the winner here. Finding #51 already showed the judge's notion of "idea" disagrees with the signal's natural grain, so optimizing AUROC against it would select for matching the judge rather than for finding signal. Primary deliverable is human-inspectable: surprise-shaded token streams and ranked highest/lowest-surprise spans with context. Any judge overlay stays faint, secondary, and explicitly non-selecting. **Choosing the layers.** I am not going to guess the layers. The tempting move is to pick a plausible shallow layer and a plausible deep layer and call it a design decision, but it's a guess, and it's an unnecessary one because the sweep is nearly free: save all 29 layer indices for every token, then fit every ordered pair (i, j) with i < j. The Gram matrix depends only on the shallow layer, so it's 29 Grams and 406 cheap solves against precomputed cross-products, not 406 independent fits — minutes of linear algebra on cached tensors, against hours of generation. Sweeping everything costs essentially nothing and removes a guess from the design. Plus the shuffle control on every pair, because a large residual can come from a large target rather than from an unpredictable one.
assistant
propose_experiment()
{
  "title": "Cross-depth predictability: does the surface text foretell the deep idea?",
  "research_question": "Is a new idea a moment where the model's deep, post-thinking representation cannot be predicted from a shallow encoding of the same text? If the surface text already determines the deep idea the model did nothing new; if the deep state departs from what the shallow encoding predicts, the model injected genuinely new reasoning.",
  "method": "During generation, store the per-token residual-stream hidden state for ALL 29 layer indices for every token. Then for every ordered layer pair (i, j) with i < j, fit a ridge map f: h_i -> h_j and define surprise = ||h_j - f(h_i)|| normalized. The Gram matrix X_i^T X_i depends only on the shallow layer, so this is 29 Grams and 406 cheap solves rather than 406 independent fits. Fit on a subset of traces and score on held-out traces (cross-trace splits) so surprise reflects generalization, not memorization. Secondary: predict the deep state k tokens ahead.",
  "controls": [
    "triviality control: predict a layer from its adjacent same-position layer, which should be near-perfect and confirm why a time-adjacent target is uninformative",
    "shuffle control: apply each trained map to mismatched shallow/deep pairs; a genuine signal must collapse under shuffling. Rank pairs by (spike - spike_shuffle), never by raw spikiness"
  ],
  "evaluation": "Qualitative-first and human-inspectable. Primary: surprise-shaded token streams rendered token-by-token, and ranked highest/lowest-surprise spans with surrounding context, for the best pairs. Secondary and explicitly non-selecting: an (i,j) sweep summary and any automated judge overlay, kept faint and never used to pick the winner.",
  "experiments": [
    "Full 406-pair sweep with cross-trace held-out evaluation, producing the (i,j) map",
    "Qualitative rendering of top pairs: shaded streams and ranked high/low spans",
    "Controls: adjacent-layer triviality and shuffle",
    "Look-ahead variant compared against same-position"
  ],
  "deliverables": [
    "(i,j) sweep summary",
    "surprise-shaded HTML and ranked span tables for top pairs",
    "control results",
    "per-token hidden states and surprise signals for reuse"
  ],
  "compute": {
    "gpu_type": "A100-80GB",
    "num_gpus": 1,
    "estimated_hours": 6
  }
}
toolpropose_experiment
{"proposal_id": "P-ed30", "status": "drafted", "compute": {"gpu_type": "A100-80GB", "num_gpus": 1, "estimated_hours": 6}, "estimated_cost_usd": 10.8, "note": "all-layer hidden states for ~40 traces estimated at ~20 GB fp16"}
assistant
launch_experiment()
{
  "proposal_id": "P-ed30"
}
toollaunch_experiment
{"run_id":"R-9f2a","status":"finished","elapsed_hours":5.4,"tokens_collected":216000,"traces":40}
assistant
read_experiment_results()
{
  "run_id": "R-9f2a",
  "section": "metrics"
}
read_experiment_results()
{
  "run_id": "R-9f2a",
  "section": "controls"
}
toolread_experiment_results
{"sweep":"406 layer pairs (i<j), ridge, cross-trace held-out evaluation, 40 traces / 216k tokens","best_region":"shallow i in 1-8 -> mid j in 8-15; best single pair i=6 -> j=12","nmse":{"adjacent_layer_pairs":0.10,"cross_depth_range":"0.3-0.8","best_pair_6_to_12":0.32},"lookahead_k4":{"nmse":0.9,"note":"weaker and noisier than same-position"},"discourse_glue_statistic":{"lowest_surprise_2pct_at_mid_depth":"~40% discourse glue (11x base rate)","lowest_surprise_2pct_at_final_layer":"18%"}}
toolread_experiment_results
{"triviality_control":{"result":"adjacent-layer (i,i+1) NMSE 0.10 vs 0.3-0.8 cross-depth","interpretation":"adjacent states are ~90% linearly recoverable from each other; a time-adjacent same-depth target would have been uninformative, as predicted"},"shuffle_control":{"final_layer_j28":{"spike_survives_shuffle":true,"true_vs_shuffle_corr":0.2,"interpretation":"final-layer spikiness is a TARGET-NORM ARTIFACT, not input-conditioned signal"},"mid_depth_j8_15":{"spike_survives_shuffle":false,"interpretation":"mid-depth spikes collapse under shuffling, i.e. genuine input-conditioned prediction"},"recommendation":"rank layer pairs by (spike - spike_shuffle), never by raw spikiness"}}
assistant
Two results here, and the second one is more important than the first. **The signal is real, and it is not where I would have guessed.** The best pairs are shallow i≈1–8 into mid j≈8–15, best single pair 6→12 at NMSE 0.32. The final layer — the obvious place to look for "the model's considered representation" — is a trap. And the triviality control confirms the redesign was necessary: adjacent-layer pairs sit at NMSE 0.10, about 90% linearly recoverable, exactly the flat uninformative signal the original time-adjacent target would have produced. **The shuffle control is doing load-bearing work.** Final-layer spikiness *survives shuffling the input* at corr ≈ 0.2 — meaning those spikes track token norm, not the specific context. Mid-depth spikiness collapses under shuffle, which is what a genuine input-conditioned signal must do. So the selection criterion is not raw spikiness but spike minus shuffled-spike. I want to look at the actual tokens before I take this anywhere, for the same reason as last time — and specifically I want to look at the *artifact* pair too, not just the winner.
read_experiment_results()
{
  "run_id": "R-9f2a",
  "section": "qualitative",
  "detail": "ranked highest and lowest surprise spans with context for the best pair 6->12, and for comparison the top spans from the shuffle-failing pair 4->28"
}
toolread_experiment_results
{"pair_6_to_12":{"highest_surprise":[{"z":7.46,"context":"...Therefore, by Banach fixed<<-point>> theorem, f has a unique fixed point..."},{"z":7.40,"context":"...Given that f(z) = |z<<|>> - 1/2|, and z = sin(..."},{"z":7.27,"context":"...f(x) = { ax^2 if x >= a<<,>> { ax + 2a if x < a..."},{"z":6.97,"context":"...maybe I can use the sho<<el>>ace formula..."},{"z":6.92,"context":"...when you have a point that reflects<< off>> a line, the shortest path..."},{"z":6.46,"context":"...the altitude to the hypoten<<use>> has some nice properties..."}],"lowest_surprise":[{"z":-3.07,"context":"...as simplified as it gets.\n\n<<Wait>>, but let me make sure..."},{"z":-3.00,"context":"...multiply by 2 each time.\n\n<<Wait>>, but hold on..."},{"z":-2.98,"context":"...Therefore, w = 6 - 5i.\n\n<<Wait>>, but let me think..."},{"z":-2.92,"context":"...f(x1) != x1.\n\n<<But>> in our case, x1 is a fixed point..."},{"z":-2.86,"context":"...triangle ABC has area 60.\n\n<<So>>, then when I..."}]},"pair_4_to_28_shuffle_failing":{"highest_surprise":[{"context":"...Therefore,<< by>> Banach fixed-point theorem..."},{"context":"...use the shoel<<ace>> formula..."},{"context":"...the altitude to the hypoten<<use>>..."}]},"lookahead_k4_top_spans":[{"context":"...Let<< me>> compute..."},{"context":"...step by step..."}]}
assistant
The 6→12 signal separates cleanly. High surprise lands on newly introduced mathematical content — named theorems and methods being invoked (Banach, shoelace, altitude-to-hypotenuse), and structural symbols that open new objects (the `|` of a nested absolute value, the comma splitting a piecewise definition). Low surprise is almost entirely discourse glue: "Wait", "So", "But". That matches the objective statistic — lowest-surprise 2% is ~40% discourse glue at mid-depth, 11× base rate. **But look at 4→28.** Banach, shoelace, hypotenuse — by eye it looks *just as good*. And the shuffle control says it is an artifact: those spikes survive scrambling the input, so they are tracking token norm, not context. This is the important lesson from this run, and it is a lesson about my own method. I pushed hard to make qualitative human inspection the primary evaluation because I didn't trust the automated judge. That was right about the judge and wrong about what inspection can do: **eyeballing cannot distinguish the genuine signal from the artifact here.** Both look like a competent researcher pointing at named theorems. The only thing that separates them is a control that has no qualitative rendering at all. So the rule going forward is: qualitative inspection tells me *what the signal means*; the shuffle control tells me *whether there is a signal*. I need both, and I select on the control, never on how plausible the bright tokens look. Any downstream method must pick its layer pair by spike-minus-shuffle. One honest limitation to carry forward: the signal fires at sub-word content granularity — "el" in shoelace, "use" in hypotenuse. It marks where new *content* enters, which is related to but not identical to where a new reasoning *strategy* begins. That's the same grain tension as finding #51, and it may matter for the eventual decoding method: forking at every new-content token is not the same as forking at genuine strategy branch points. Also worth noting the look-ahead variant (predict 4 tokens ahead, NMSE ~0.9) drifts toward "about to start a step" cues and is clearly weaker — same-position surprise is the cleaner object.
assistant
record_finding()
{
  "title": "Cross-depth surprise is a genuine novelty signal at early->mid depth; the final layer is a norm artifact",
  "statement": "Predicting a token's deep hidden state from a shallow encoding of the same token yields a real input-conditioned novelty signal, best at i in 1-8 -> j in 8-15 (best pair 6->12, NMSE 0.32). High surprise marks newly introduced mathematical content; low surprise marks discourse glue. Final-layer targets produce spikes that survive shuffling and must not be used.",
  "evidence": "406-pair ridge sweep over 40 traces / 216k tokens, cross-trace held-out. Triviality control: adjacent-layer NMSE 0.10 vs 0.3-0.8 cross-depth. Shuffle control: j=28 spikes survive shuffling (corr 0.2); mid-depth spikes collapse. Lowest-surprise 2% is ~40% discourse glue at mid-depth (11x base rate) vs 18% at final layer. Look-ahead k=4 NMSE ~0.9.",
  "caveats": "Qualitative inspection CANNOT distinguish the genuine mid-depth signal from the final-layer artifact - both look equally plausible by eye. Always select layer pairs by (spike - spike_shuffle). Signal fires at sub-word content granularity, which is finer than a reasoning strategy."
}
toolrecord_finding
{"finding_id":52,"recorded":true}
assistant
There is a conceptual problem with what I have, and it is about the reference class rather than the estimator. The map is trained offline, pooled across a corpus, so "surprising" currently means atypical relative to how this model reasons *in general*. What I actually need, for generating diverse candidates on one problem, is novelty relative to what this trace has already done. A trace looping over eight near-identical cases is doing something highly repetitive and the offline map cannot see it — every case looks equally typical. Meanwhile the first time a trace introduces a technique is exactly what I want flagged, and offline only flags it if that technique is rare corpus-wide. So: learn the map online, per trace. Prequential error — predict the deep state from the shallow state with the map as it stands, score *before* updating, then fold the token in. The predict-then-update ordering is what makes it a novelty score rather than a fit statistic: the predictor is never scored on a token it has already learned from. Recurring structure gets predicted well after the trace has shown it once or twice, so its surprise decays; genuinely new structure spikes because the map hasn't adapted. A forgetting factor lets it track the trace's current mode rather than its global average. It also needs no offline corpus at test time, which the eventual decoding method will want. Now, how much to run. There is an obvious full study here — sweep the forgetting factor, compare cold versus warm initialization, check several layer pairs, measure motif-adaptation curves. I'm not running it, because all of those are questions about how best to configure a mechanism whose basic viability is unestablished. Configuration questions are only worth answering if there is something to configure; if online prequential surprise is noise, every one of them was wasted. So: one layer pair, the validated 6→12. Warm start only, so there's no burn-in confounding the read. Two update rules side by side — RLS and NLMS — not as an ablation but as a reliability check: they are independent learning rules, so if they agree, what they are measuring is a property of the data rather than of one rule. And the shuffle control, which finding #52 established as the thing that actually decides whether a signal is real.
assistant
propose_experiment()
{
  "title": "Online prequential surprise - quick signal check",
  "research_question": "Does an online, per-trace predictor - one that learns during generation what this trace's reasoning usually looks like - produce a usable new-idea surprise signal at all? Minimal proof-of-signal; configuration questions are deliberately deferred.",
  "method": "At each token t, on the validated shallow->deep direction from finding #52 (i=6 -> j=12): (1) predict hhat_j(t) = W_{t-1} . h_i(t); (2) score s(t) = ||h_j(t) - hhat_j(t)||^2 / ||h_j(t)||^2 BEFORE any update; (3) update W with this token. Predict-then-update is what makes s(t) a novelty score: the predictor is never scored on a token it has already learned from. Warm-start from the pooled offline ridge map so there is no burn-in confounding the read; reset per trace.",
  "controls": [
    "shuffle control (mandatory): mismatched i/j pairs must collapse the signal",
    "RLS vs NLMS run side by side - not an ablation but a reliability check: two independent update rules agreeing implies the signal is in the data, not in the learning rule"
  ],
  "evaluation": "Ranked highest/lowest-surprise token contexts as readable text for both update rules, shown next to the offline signal, across a handful of MATH-500 L5 traces. Plus the shuffle control.",
  "experiments": [
    "E1 online surprise, RLS vs NLMS at i=6 -> j=12, warm-started: ranked high/low spans next to offline",
    "E2 reliability and control: rule-vs-rule agreement plus the shuffle control"
  ],
  "deliverables": [
    "ranked span report",
    "agreement statistics",
    "shuffle-control result"
  ],
  "compute": {
    "gpu_type": "A100-80GB",
    "num_gpus": 1,
    "estimated_hours": 3
  }
}
toolpropose_experiment
{"proposal_id": "P-b7f3", "status": "drafted", "compute": {"gpu_type": "A100-80GB", "num_gpus": 1, "estimated_hours": 3}, "estimated_cost_usd": 5.4}
assistant
launch_experiment()
{
  "proposal_id": "P-b7f3"
}
toollaunch_experiment
{"run_id":"R-f520","status":"finished","elapsed_hours":2.4,"traces":8,"completion_tokens":26115}
assistant
read_experiment_results()
{
  "run_id": "R-f520",
  "section": "summary"
}
toolread_experiment_results
{"setup":"8 MATH-500 L5 traces, R1-Distill-1.5B, temp 0.6 / top-p 0.95, <=4096 tokens, 26,115 completion tokens. Predict hhat12 = W.h6, score s = ||h12 - hhat12||^2 / ||h12||^2 BEFORE updating W (prequential), warm-started from pooled offline ridge, reset per trace. RLS and NLMS (online SGD) run side by side.","reliability":{"spearman_rho_RLS_vs_NLMS":0.94,"interpretation":"two independent update rules agree; not a rule artifact"},"shuffle_control":{"result":"PASSES","mean_surprise_true":0.12,"mean_surprise_shuffled":1.0,"true_vs_shuffle_rho":{"NLMS":0.10,"RLS":0.22}},"qualitative":{"high_online_surprise":"onset of a new local computation or symbol - the first time the trace does a thing","low_online_surprise":"within-trace repeated templates - the model looping over near-identical cases, which the online predictor has adapted to"},"online_vs_offline_cross_correlation":"rho 0.33-0.42 - meaningfully different objects; offline tracks the global content-vs-glue split, online tracks novelty relative to what this trace has already done","stability_warning":{"issue":"RLS forgetting factor lambda_f=0.99 is UNSTABLE at D=1536: mean surprise >1 and true-vs-shuffle rho 0.5-0.6, i.e. an artifact that survives shuffling","fix":"lambda_f=0.999 with posterior warm-start covariance P0 = inv(X^T X + lambda I)","note":"NLMS stable across mu in {0.2, 0.05, 0.01}; forgetting factor is the most important knob"}}
assistant
Online surprise works, and the diagnostics are the reason I believe it rather than the headline. Two independent update rules — RLS and NLMS — agree at ρ=0.94, so what they are measuring is a property of the data and not of a particular learning rule. The shuffle control passes cleanly: scrambling input against targets sends mean surprise from 0.12 to ~1.0 and drops true-versus-shuffle correlation to ~0.10. That is the failure mode that killed the final-layer pair last run, and this signal does not have it. What it fires on is different from the offline map, in exactly the way the reframing predicted. High online surprise is the *onset of a new local computation* — the first time the trace does a thing. Low online surprise is within-trace repeated templates, the model grinding through near-identical cases. Online and offline only correlate at ρ≈0.33–0.42, so these are genuinely different objects: offline measures novelty against the corpus, online measures novelty against this trace's own history. The second is the one relevant to diversifying candidates for a single problem. The stability finding matters more than it looks. RLS at λ_f=0.99 produced mean surprise >1 and a true-versus-shuffle correlation of 0.5–0.6 — that is the artifact signature. Without the shuffle control I would have read those large surprise values as a strong signal and built on top of a broken estimator. That is now twice that the shuffle control has caught something eyeballing could not. λ_f=0.999 with the posterior warm-start covariance fixes it; NLMS was stable everywhere, so NLMS is what I'll use downstream.
assistant
record_finding()
{
  "title": "Online prequential surprise is a validated, self-calibrating, per-trace novelty signal",
  "statement": "An online predict-then-update linear map on the L6->L12 cross-depth direction produces a real input-conditioned novelty signal with no offline corpus needed at test time. High surprise = onset of a new local computation; low surprise = within-trace repeated templates.",
  "evidence": "8 MATH-500 L5 traces, 26,115 tokens. Spearman rho(RLS, NLMS) = 0.94. Shuffle control passes: mean surprise 0.12 -> ~1.0, true-vs-shuffle rho 0.10 (NLMS) / 0.22 (RLS). Cross-correlation with the offline map only rho 0.33-0.42.",
  "caveats": "RLS with lambda_f=0.99 is UNSTABLE at D=1536 and produces an artifact that survives shuffling (rho 0.5-0.6). Use lambda_f=0.999 with posterior warm-start covariance P0=inv(X^T X + lambda I), or use NLMS (mu ~ 0.05), which was stable across all step sizes tested. The forgetting factor is the most sensitive knob."
}
toolrecord_finding
{"finding_id":53,"recorded":true}
assistant
Before I design the payoff run I want the exact configuration the last three runs established, rather than reconstructing it from memory and quietly getting the forgetting factor wrong.
search_findings()
{
  "query": "validated online surprise configuration, layer pair, update rule, stability",
  "max_results": 5
}
toolsearch_findings
{"results": [{"id": 51, "title": "Hidden-state change-points are a real but computation-grained idea segmenter", "use": "detector fires at new-equation grain, recall collapses on dense traces"}, {"id": 52, "title": "Cross-depth surprise is genuine at early->mid depth; the final layer is a norm artifact", "use": "use pair L6->L12; select layers by (spike - spike_shuffle), never by raw spikiness"}, {"id": 53, "title": "Online prequential surprise is a validated, self-calibrating, per-trace novelty signal", "use": "use NLMS online SGD mu ~ 0.05 warm-started from a pooled offline ridge map; RLS at lambda_f=0.99 is UNSTABLE at D=1536 and produces an artifact that survives shuffling"}]}
assistant
The detector is de-risked enough. Three runs have established that the signal is real, where it lives, and which controls keep me honest about it. The remaining ablations would sharpen a signal I already trust; they would not change whether the research question has a yes or a no. Time to test the thing I came here to test. Four design constraints, each fixing a specific way this could produce a misleading answer. **Report Pass@k against total generated tokens, not just against k.** Forking shares prefixes via KV cache and therefore spends tokens differently from i.i.d. sampling. Without the token-matched axis, a result that is really "spent more compute per candidate" reads as an efficiency win. **Objective scoring only** — MATH-500 with normalized symbolic answer matching, no LLM judge anywhere in the scoring path. I've already spent two runs learning not to trust a judge on the signal side; I'm not introducing one on the outcome side. **Two baselines beyond plain sampling**, because the claim I want to make is specifically about the *internal* signal. Entropy-fork uses the identical branching tree but forks at high output-entropy positions, testing whether internal surprise beats a cheap output-side heuristic that needs no hidden states at all. Random-fork uses the identical tree with random branch positions, isolating the value of *where* we branch from the value of prefix-sharing tree structure itself. Without both, a win would be unattributable — prefix sharing alone will make any fork method look token-cheaper than i.i.d. **One benchmark, not two.** Splitting a fixed budget across MATH-500 and AIME24 buys a weaker read on both, and AIME24 at 1.5B will be mostly zeros. All of it on MATH-500 hard, where root Pass@1 leaves room for branching to matter. And one deliverable that isn't a metric: log every candidate's full reasoning trajectory as a first-class output — complete decoded text, tree structure with fork positions and parent/child links, per-token surprise trace, extracted answer, correctness, as structured per-problem JSON. The Pass@k number answers whether this works. It does not answer why, and the why-question I will certainly want next is whether surprise-guided branching produces genuinely diverse *reasoning* rather than merely diverse answers. Those come apart — candidates can converge on one answer through different routes, or reach different wrong answers by the same flawed route. If I don't log now, answering that later means re-running the whole thing.
assistant
propose_experiment()
{
  "title": "Surprise-guided branching for Pass@k",
  "research_question": "Does forking reasoning trajectories at online-surprise new-idea tokens improve Pass@k efficiency over standard stochastic sampling and over cheap heuristic branch-point baselines?",
  "method": "Online surprise during decoding via forward hooks on L6/L12; predict hhat12 = W.h6, score prequential surprise before updating W. Stable configuration from finding #53: NLMS online SGD (mu ~ 0.05), warm-started from a pooled offline ridge map fit on held-out traces, reset per root trajectory; a running median/MAD z-score flags spikes. Surprise-fork: generate a root trajectory, fork at its top-B surprise spikes, sample divergent continuations sharing the root prefix via KV cache, recurse to k leaves. Every generated token counts toward the efficiency axis.",
  "controls": [
    "iid sampling, k independent completions at temp 0.6 / top-p 0.95",
    "entropy-fork: identical tree, fork at high next-token output-entropy positions - tests whether the internal signal beats an output-side heuristic needing no hidden states",
    "random-fork: identical tree, random fork positions - isolates where we branch from the tree structure itself",
    "pre-flight: verify hook-based L6/L12 extraction is numerically identical to the reference indexing the warm-start map assumes"
  ],
  "evaluation": "Pass@k as a function of total generated tokens per problem (the matched-budget efficiency curve), plus Pass@k at fixed k=1..16 with the unbiased estimator. Correctness by exact/normalized math-answer matching - no LLM judge anywhere in the scoring path. Mechanism checks: mean surprise-z at chosen fork points per method, distinct-answer count, answer entropy, tail reasoning divergence.",
  "experiments": [
    "E1 main Pass@k: surprise-fork vs iid vs entropy-fork vs random-fork on MATH-500 L5",
    "E2 branch-point value: the three fork rules head to head on the identical tree",
    "E3 diversity mechanism: distinct answers, answer entropy, fork-vs-spike alignment"
  ],
  "deliverables": [
    "Pass@k vs token-budget curves and fixed-k tables",
    "diversity diagnostics",
    "FULL per-candidate trajectory logs: complete decoded text and token ids, tree structure with fork positions and parent/child links and shared prefixes, per-token online-surprise trace and chosen spike positions, extracted answer, correctness - structured per-problem JSON for every method"
  ],
  "data": "MATH-500 hard/L5 subset only",
  "compute": {
    "gpu_type": "A100-80GB",
    "num_gpus": 1,
    "estimated_hours": 8
  }
}
toolpropose_experiment
{"proposal_id": "P-591b", "status": "drafted", "compute": {"gpu_type": "A100-80GB", "num_gpus": 1, "estimated_hours": 8}, "estimated_cost_usd": 14.4}
assistant
launch_experiment()
{
  "proposal_id": "P-591b"
}
toollaunch_experiment
{"run_id": "R-f381", "status": "running", "preflight": {"hook_extraction_check": "L6/L12 hook extraction bit-identical to reference indexing (0.0 max-abs diff); warm-start map applies correctly", "green_pilot": "passed end-to-end at small k on 3 problems; JSON schema and analyzer both work", "mechanism_signal": "surprise-chosen fork points at mean surprise-z 6.9 vs 0.25 (entropy) and 0.36 (random)", "calibration": "R1-Distill-1.5B needs long CoT on L5: at max_new=6144 only 4/10 problems finished, finishing traces 3880-4967 tokens", "measured_throughput": "~20 min/problem at k=16, max_new=5120, continuation batch of 61 sequences; peak memory ~37 GB", "implication": "the planned 134-problem set would take ~45 h against an 8 h budget", "awaiting": "budget re-scoping decision before committing"}}
assistant
The pre-flight checks are what I wanted: hook extraction is bit-identical to the reference indexing, so the warm-start map applies, and the mechanism is already visibly working — surprise forks at mean z 6.9 against 0.25 for entropy and 0.36 for random. But measured throughput puts the planned run at ~45 hours against an 8 hour budget, so something has to give. Three axes I could cut, and they are not equivalent. Cutting **k** destroys the measurement — k is the x-axis of the result. Not an option. Cutting the **per-candidate token budget** is the tempting one because it scales throughput directly, and it is the dangerous one. Calibration says L5 solutions run 3880–4967 tokens when they finish at all; truncating below that doesn't just lower scores, it lowers them *differentially* — whichever method produces longer traces gets penalized more. That is a confound sitting directly on top of the comparison I am trying to make. Cutting **N** only widens the error bars. It costs precision, not validity. That's the one to cut. So: hold k=16, set max_new=4096, drop to 32 problems. 4096 will still truncate the longest solutions and I want to be explicit that this lowers absolute Pass@k — but it is applied identically to all four methods, so it doesn't bias the between-method comparison, which is the quantity I actually care about. I'll have it report truncation rate per method so I can confirm that uniformity rather than assume it.
steer_experiment()
{
  "run_id": "R-f381",
  "instruction": "Re-scope by cutting N only. Hold k=16 and hold the per-candidate token budget; set max_new=4096 and N=32 MATH-500 L5 problems, checkpointed per problem. Do NOT cut k and do NOT reduce the per-candidate budget below 4096. Apply the identical max_new to all four methods. Rationale: k is the x-axis of the result, so cutting it destroys the measurement; cutting the per-candidate budget truncates L5 solutions and would differentially penalise whichever method produces longer traces, confounding the comparison. Cutting N only widens the error bars. 4096 will still truncate the longest solutions - that is acceptable because it is applied uniformly, so it lowers absolute Pass@k without biasing the between-method comparison. Report the truncation rate per method so I can confirm it is uniform."
}
toolsteer_experiment
{"run_id": "R-f381", "accepted": true, "new_config": {"k": 16, "max_new": 4096, "n_problems": 32, "estimated_hours": 7, "checkpointed": "per problem"}, "note": "will report per-method truncation rate alongside results"}
assistant
read_experiment_results()
{
  "run_id": "R-f381",
  "section": "metrics",
  "detail": "partial results on problems completed so far, including mechanism checks"
}
toolread_experiment_results
{"progress":"22 of 32 problems generated; analyzers run on the first 21","mechanism_check":{"mean_surprise_z_at_chosen_fork_points":{"surprise_fork":5.8,"entropy_fork":0.0,"random_fork":0.0},"interpretation":"surprise-fork branches exactly where intended and distinctly from the baselines"},"pass_at_k":{"estimator":"unbiased, 21 MATH-500 L5 problems, k=16 samples per problem","table":[{"k":1,"surprise":0.51,"entropy":0.54,"random":0.45,"iid":0.45},{"k":4,"surprise":0.64,"entropy":0.65,"random":0.61,"iid":0.66},{"k":8,"surprise":0.68,"entropy":0.67,"random":0.69,"iid":0.73},{"k":16,"surprise":0.71,"entropy":0.67,"random":0.76,"iid":0.76}]},"diversity_diagnostics":{"distinct_answers_surprise":0.95,"distinct_answers_others":1.10,"answer_entropy_surprise":0.024,"answer_entropy_others":0.06,"tail_reasoning_divergence":"~0.81, essentially identical across all fork rules"},"token_cost_at_k16":{"all_fork_methods":"42-45k","iid":"54k","note":"~20% cheaper, attributable to shared-prefix KV reuse, not to the surprise signal"},"root_pass_at_1":0.48}
assistant
This is a negative result, and I want to state it without hedging. **The mechanism works and the hypothesis fails.** Surprise-fork places its branches at mean surprise-z ≈ 5.8 versus ≈ 0 for entropy and random, so it is forking exactly where the signal says a new idea begins — the implementation is doing what it was designed to do. And it does not improve Pass@k. At k=16 it reaches 0.71 against 0.76 for both plain i.i.d. sampling and random-fork. It is *behind*, not ahead. **The diagnosis is coherent rather than mysterious**, which makes it more informative than a noisy null. Surprise-fork produces the *fewest* distinct answers (0.95 vs 1.10) and the *lowest* answer entropy (0.024 vs ~0.06), while tail reasoning divergence is essentially identical across all fork rules (~0.81). So forking at surprise peaks makes candidates mildly *converge*. The explanation I find most plausible: a high-surprise token is a moment of high model commitment. The spike appears because the model has just introduced a specific new object — the decision that produced it is already behind us and the distribution right there is peaked, so resampling at that position barely moves the continuation. I have been branching at symptoms of decisions rather than at decisions. **One real but non-specific win:** all fork methods use 42–45k tokens at k=16 against i.i.d.'s 54k, so branching is ~20% cheaper. That is a property of shared-prefix KV reuse and would hold for random-fork too. It is not evidence for the surprise signal. Caveats I'm holding: N=21 of a planned 32 with wide error bars, root Pass@1 is 0.48 so many problems are decided regardless of branching, and this is one branch-point policy at one layer pair. Now the question of whether to let it finish. The remaining 11 problems will tighten the intervals, but the sign of the effect is not close — surprise-fork is behind i.i.d. at every k above 1, and the diversity diagnostics point the same direction as the Pass@k table rather than contradicting it. Nothing in the last third is going to turn a mild convergence into a win. And the diagnosis has already handed me a specific, testable alternative that needs the same GPU. Finishing the run buys precision on a number I am not going to act on; stopping it buys a shot at the mechanism that the diagnosis says should work.
assistant
stop_experiment()
{
  "run_id": "R-f381",
  "reason": "Direction of the effect is settled at N=21 and the diversity diagnostics corroborate rather than contradict it; the remaining 11 problems would tighten intervals on a result I will not act on. Reallocating compute to the mechanism the diagnosis implies. Trajectory logs for the completed problems are retained for reuse as a baseline on an identical problem set."
}
toolstop_experiment
{"run_id":"R-f381","status":"stopped","retained_artifacts":"per-candidate trajectory logs, tree structures, per-token surprise traces, answers and correctness for 21 MATH-500 L5 problems across all four methods"}
assistant
record_finding()
{
  "title": "Discrete forking at surprise peaks does not improve Pass@k and mildly converges candidates",
  "statement": "Forking reasoning trajectories at online-surprise spikes places branches correctly (mean surprise-z 5.8 vs ~0 for heuristic baselines) but does not lift Pass@k; it ends slightly behind iid sampling and random-fork at k=16 and produces the fewest distinct answers and lowest answer entropy. Proposed mechanism: a surprise spike is a symptom of a token the model has ALREADY committed to, so the distribution at that position is peaked and resampling there moves little.",
  "evidence": "21 MATH-500 L5 problems, k=16, unbiased estimator. Pass@16: surprise 0.71, entropy 0.67, random 0.76, iid 0.76. Distinct answers 0.95 vs 1.10; answer entropy 0.024 vs ~0.06; tail reasoning divergence ~0.81 across all fork rules. All fork methods 42-45k tokens vs iid 54k at k=16 (prefix-sharing, not signal-attributable). Root Pass@1 0.48.",
  "caveats": "N=21 with wide error bars; one branch-point policy at one layer pair. The negative result concerns discrete forking AT peaks, not the surprise signal itself, which remains validated by finding #53."
}
toolrecord_finding
{"finding_id":54,"recorded":true}
assistant
Take the diagnosis seriously and it points somewhere specific rather than at "try a different threshold." The discrete method failed because it is **reactive**. It waits for a spike, and a spike is evidence that a commitment has already been made. Everything downstream of that position is conditioned on the commitment, which is why resampling there barely moves and why candidates converged. The non-reactive counterpart: instead of choosing *where* to intervene, intervene *everywhere*, weakly. I already have a continuous per-token surprise estimate — I have been thresholding it into spikes and throwing the rest away, which is the same mistake I made with change-points in the first run. Use it to shape the sampling distribution at every step, tilting toward continuations that would be surprising. That acts *at* the decision point rather than after it, which is precisely the thing the discrete method structurally could not do. The obvious risk is that "surprising for its own sake" is off-distribution garbage that destroys correctness. So the deliverable is not a single number but a tilt-strength sweep — a diversity-versus-accuracy tradeoff curve, with λ=0 recovering the baseline exactly, and the question being whether any λ>0 lifts Pass@k. Let me check what's known about the mechanism family first.
search_literature()
{
  "query": "controlled decoding auxiliary per-token signal reshaping logits classifier guided attribute steered contrastive decoding activation steering representation engineering",
  "categories": [
    "cs.CL",
    "cs.LG"
  ],
  "max_results": 10
}
toolsearch_literature
{"results":[{"title":"FUDGE: Controlled Text Generation With Future Discriminators","finding":"An auxiliary per-token predictor reshapes logits at each step to steer generation toward an attribute.","relevance":"high - same mechanism family"},{"title":"PPLM / GeDi","finding":"Attribute-conditioned decoding by gradient or discriminator-guided logit modification; mild guidance preserves fluency, strong guidance degrades it.","relevance":"high - establishes the tilt-strength tradeoff is real"},{"title":"Contrastive Decoding","finding":"Per-token logit adjustment from a second signal improves reasoning without retraining.","relevance":"high"},{"title":"ActAdd / Representation Engineering (RepE)","finding":"Adding a direction vector into the residual stream at an intermediate layer steers generation; the residual stream is additive and the effect propagates through later layers to the unembedding.","relevance":"high - mechanism for intervening in activation space rather than logit space"}],"notice":"No prior work uses an internal cross-depth novelty residual as the steering attribute."}
assistant
The obvious implementation of continuous tilting is a lookahead search, and it is unnecessary — seeing why collapses the cost. That implementation is: for each of the top-m candidate tokens, run a forward pass to measure the surprise it would induce, then reshape the logits by those scores. That is m extra forwards per step, spent purely on *measuring* which tokens happen to have a property. But the surprise residual `r(t) = h12(t) − W·h6(t)` is not a scalar score — it is a **direction in activation space**, living in the same 1536-dimensional space as the hidden state itself. I do not have to search for tokens that lie along it. I can add it. Because the residual stream is additive and the LM head is linear, injecting `g = λ·r̂` at layer 12 and letting layers 13→28 propagate it reshapes the *entire* next-token distribution — inside the single forward pass I am already running. That is ActAdd/RepE, and it is the principled version of "push the state toward more surprise": predicted and actual live in one space, so the surprise residual *is* the steering vector. Roughly 1× decode cost instead of 2×, which keeps this on A100. What that mechanism then requires: **A matched-norm random direction**, as a mandatory control. This is the steering analog of the shuffle control. Injecting any vector of sufficient magnitude will perturb the distribution and produce some change in diversity; without a random direction at matched norm I cannot claim a Pass@k gain came from the *surprise* direction specifically rather than from generic activation perturbation. The shuffle control has already caught two artifacts I would otherwise have believed, so I am not running the steering analog without it. **An estimator that updates on the unsteered h12** — the model's true computation — so steering shapes the choice without corrupting the estimator producing the direction. Otherwise the signal feeds on its own intervention and drifts. **A λ sweep over both signs, not a single setting.** Strong tilt should produce off-distribution degradation; the deliverable is the diversity-versus-accuracy tradeoff curve, with λ=0 recovering the baseline exactly and the question being whether any λ>0 lifts Pass@k. The negative-λ arm is a sign check: if steering away from surprise helps as much as steering toward it, the direction is not doing what I think. Two things I am deliberately not running. I already have the i.i.d. and entropy baselines from the discrete-fork run, which logged full trajectories before I stopped it — re-running them would spend compute reproducing numbers I have. But reusing them is **only valid on the same problem set**: those logs cover a specific set of MATH-500 L5 problems, and evaluating the steering conditions on anything else makes the comparison meaningless. That is an easy mistake to make while believing you are being efficient. So N stays at those ~32 rather than the larger set the freed compute would buy — the fixed evaluation set is worth more than the extra problems. And I'm excluding the exact-lookahead faithfulness reference and the KV write-back ablation. The first tells me how well cheap steering approximates the exact surprise-tilt, which is a question about my approximation and not about whether the method works — if steering lifts Pass@k I will care regardless. The second is configuration. Neither is on the critical path to the answer, so all compute goes to the novel conditions.
assistant
propose_experiment()
{
  "title": "Surprise-direction activation steering for Pass@k",
  "research_question": "Discrete forking at surprise peaks did not improve Pass@k and mildly converged candidates, because a spike is a symptom of a token the model has already committed to - the decision is behind us and the distribution there is peaked. Does acting AT the decision instead, by continuously steering the residual stream along the surprise direction at every step, diversify reasoning and lift Pass@k?",
  "method": "The surprise residual r(t) = h12(t) - W.h6(t) is a direction in activation space, in the same 1536-dim space as the hidden state. Rather than searching over candidate tokens for ones that induce surprise, inject g = lambda * rhat at layer 12 and let layers 13->28 propagate it: because the residual stream is additive and the LM head is linear, this reshapes the entire next-token distribution within the single forward pass already being run - ~1x decode cost, no lookahead. This is activation steering / representation engineering (ActAdd, RepE) with the validated internal novelty signal as the steering attribute. Two variants: propagated steering and direct-unembedding steering. Implementation constraint: the online predictor updates on the UNSTEERED h12 (the model's true computation), so steering shapes the choice without corrupting the estimator that produces the direction.",
  "controls": [
    "MANDATORY: matched-norm random-direction steering - the steering analog of the shuffle control. Any sufficiently large injected vector perturbs the distribution, so without this a Pass@k gain cannot be attributed to the surprise direction rather than to generic activation perturbation",
    "lambda = 0 must reproduce the unsteered baseline exactly",
    "negative lambda (steer away from surprise) as a sign check",
    "iid and entropy baselines are NOT re-run: reuse the retained per-candidate trajectory logs from the stopped discrete-fork run"
  ],
  "evaluation": "Pass@k vs token budget across the lambda sweep. The primary output is the diversity-vs-accuracy tradeoff curve, not a single number - strong tilt is expected to produce off-distribution degradation, and the question is whether any lambda > 0 lifts Pass@k.",
  "experiments": [
    "E1 lambda sweep over both signs, propagated steering, Pass@k vs token budget",
    "E2 direct-unembedding steering variant",
    "E3 matched-norm random-direction control across the same lambda magnitudes",
    "E4 diversity diagnostics per lambda: distinct answers, answer entropy, tail reasoning divergence"
  ],
  "constraints": [
    "Evaluate on the IDENTICAL ~32 MATH-500 L5 problems covered by the retained logs. Reusing those baselines is only valid on the same problem set, so N stays at 32 rather than the larger set the freed compute would allow - the fixed eval set is worth more than the extra problems.",
    "Excluded as not on the critical path: exact-lookahead faithfulness reference (a question about the approximation, not about whether the method works) and the KV write-back ablation (configuration)."
  ],
  "compute": {
    "gpu_type": "A100-80GB",
    "num_gpus": 1,
    "estimated_hours": 6
  }
}
toolpropose_experiment
{"proposal_id": "P-8527", "status": "drafted", "compute": {"gpu_type": "A100-80GB", "num_gpus": 1, "estimated_hours": 6}, "estimated_cost_usd": 10.8}
assistant
launch_experiment()
{
  "proposal_id": "P-8527"
}
toollaunch_experiment
{"run_id":"R-58fb","status":"running","title":"Surprise-direction activation steering for Pass@k"}
assistant
Where the investigation stands, and what would kill it. **Established.** Reasoning models carry a real, input-conditioned novelty signal in their internal representations. It is not where you would first look for it: it lives in the *cross-depth* gap between an early encoding and a mid-depth representation (best pair L6→L12), not in the final layer, whose apparent spikiness is a target-norm artifact that survives shuffling. It can be estimated fully online during generation, with no offline corpus at test time, via a predict-then-update linear map, and two independent update rules agree at ρ=0.94 on what it says. High surprise marks the onset of new computation; low surprise marks discourse glue and within-trace repetition. **Refuted.** The obvious way to convert that signal into candidate diversity — fork trajectories at surprise peaks — does not improve Pass@k. It reaches 0.71 at k=16 against 0.76 for plain i.i.d. sampling, and it mildly *reduces* candidate diversity rather than increasing it. The mechanism was verified to be doing what it was designed to do (forks land at surprise-z 5.8 vs ~0 for baselines), so this is a failure of the hypothesis, not of the implementation. **The diagnosis, which is the actual contribution so far.** A surprise spike is a symptom of a decision the model has already made. Intervening there is intervening downstream of the branch point, where the distribution is peaked. This is why the reactive, discrete method could not work, and it predicts that a method acting *at* the decision — continuously, at every step — could. **Live test.** Steering the residual stream along the surprise direction, λ sweep over both signs, on the identical 32 problems as the discrete run. **What would kill the whole line of work, stated in advance so I don't rationalize afterward:** if matched-norm random-direction steering produces the same Pass@k curve as surprise-direction steering, then any effect is generic activation perturbation and the internal novelty signal contributes nothing to decoding — regardless of how clean finding #52 and #53 look. That control decides it. Given that the shuffle control has already caught two artifacts I would otherwise have believed, I expect it to be the most informative number in this run.

labels

successful_investigation:true
primary_conclusion:Reasoning models carry a genuine online-measurable novelty signal in the cross-depth gap between shallow and mid-depth representations (L6->L12), but discretely forking candidate trajectories at that signal's peaks does not improve Pass@k and mildly converges…
negative_result_reported:true
negative_result_diagnosed_mechanistically:true
assumption_derisked_before_building:true
trivial_prediction_target_rejected:true
weak_automatic_metric_demoted_for_human_inspection:true
limits_of_human_inspection_acknowledged:true
artifact_control_mandated_and_load_bearing:true
scope_cut_to_next_decision:true
compute_reallocated_from_settled_question:true
settled_baselines_reused_not_rerun:true
eval_set_held_fixed_across_conditions:true
falsification_condition_preregistered:true
unsupported_claims_present:false
all_quantitative_claims_traceable_to_tool_results:true

steering_moves