Skip to content

fix(vision-suite): summarize_reps reports the context-ladder rung; retire summarize_lowtemp - #197

Merged
glennneuber merged 3 commits into
mainfrom
fix/summarize-reps-reports-the-rung
Aug 18, 2026
Merged

glennneuber merged 3 commits into
mainfrom
fix/summarize-reps-reports-the-rung

Conversation

@glennneuber

Copy link
Copy Markdown

Two ADRs already settled most of this

ADR 0029 makes THINK_TEMPERATURE default 0 and records the 0.01 variant as measured and rejected. SPEC H4a makes the context-ladder rung a result, not plumbing — and run_engine_compare.sh now refuses (exit 2) a CTX_MAX that leaves no rung above the think-on start.

What was actually missing

summarize_reps.py never rendered the rung, though ADR 0029 requires think-on tables to report it, H4a calls it a throughput fact about the model, and ADR 0012 rule 8 says the reported num_ctx is the final successful rung and a first-class result. Its own test fixtures carry num_ctx and nothing read it.

| metric | qwen3_6_35b-a3b-nvfp4_thinkon (n=1) | nemotron3_33b-bf16_thinkon (n=1) |
|---|---|---|
| **num_ctx rung** | 65536 | 32768 |
| **num_predict** | 57344 | 24576 |
| scene bbox IoU | 0.936 | 0.872 |

The rung goes first, before the metrics, because it conditions everything under it: two arms at different rungs are not comparable on tok/s or req/h at all — KV size drives decode speed — so a table without it invites the one comparison it cannot support.

Distinct values within an arm render a/b ⚠ rather than collapsing to the first: repeats of one arm are supposed to sit at one rung, and more than one means it escalated between repeats.

Retires summarize_lowtemp.py

Its arm is dead under ADR 0029. The three things it did that summarize_reps didn't — capped-cell exclusion, num_ctx columns, a no result count — are unnecessary once the ladder always runs: a cell either finishes at some rung, which is the result, or exhausts the ladder, which the runner reports as NOT CONVERGED.

Four references repointed. The negative-result document it rendered stays — it's the evidence ADR 0029 rests on.

Verified

test_summarizers.py 9/9.

🤖 Generated with Claude Code

glennneuber and others added 2 commits August 18, 2026 09:42
A remote agent could not reach :11436 while the server was up and answering
locally. Cause: OLLAMA_HOST was hardcoded to 127.0.0.1, inherited from the
original /tmp restart script, so the listener was loopback-only —
`TCP 127.0.0.1:11436`, not `TCP *:11436`. The failure is silent from the host,
where curl succeeds and nothing looks wrong.

BIND now defaults to 0.0.0.0, with OLLAMA_BIND to narrow it. Requested
explicitly; the script states what it means rather than leaving it implicit:
ollama has no authentication, so every interface this binds reaches
/api/generate, /api/pull, /api/delete and the whole model store with no
credential. OLLAMA_BIND=127.0.0.1 restores loopback-only, and that is the right
setting on any host whose network you do not control.

Verified: listener is now TCP *:11436, and the version endpoint answers on both
127.0.0.1 and the LAN address 10.1.1.84.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…tire summarize_lowtemp

TWO ADRs I HAD NOT READ ALREADY SETTLED THIS. ADR 0029 makes THINK_TEMPERATURE
default 0 and records the 0.01 variant as measured and rejected, and SPEC H4a
makes the context-ladder rung a result rather than plumbing — the runner now
refuses (exit 2) a CTX_MAX that leaves no rung above the think-on start, which
is exactly the pin I had armed on the deferred bf16 cell an hour earlier.

What was actually missing: summarize_reps.py never rendered the rung, though
ADR 0029 requires think-on tables to report it, SPEC H4a calls it a throughput
fact about the model, and ADR 0012 rule 8 says the reported num_ctx is the final
successful rung and a first-class result. Its own test fixtures carry num_ctx
and nothing read it.

Adds **num_ctx rung** and **num_predict** as the first two rows, before the
metrics, because they condition everything under them: two arms measured at
different rungs are not comparable on tok/s or req/h at all — KV size drives
decode speed — so a table without the rung invites the one comparison it cannot
support. Distinct values within an arm render as "a/b ⚠" rather than collapsing
to the first, because repeats of one arm are supposed to sit at one rung and
more than one means it escalated between repeats.

Retires summarize_lowtemp.py. Its arm is dead under ADR 0029, and the three
things it did that summarize_reps did not — capped-cell exclusion, num_ctx
columns, a no-result count — are unnecessary once the ladder always runs: a cell
either finishes at some rung, which is the result, or exhausts the ladder, which
the runner reports as NOT CONVERGED. Four references repointed. The
negative-result document it rendered stays; it is the evidence ADR 0029 rests on.

Verified: test_summarizers.py 9/9, and the rung row renders 65536/57344 for
qwen3.6-nvfp4 think-on against 32768/24576 for nemotron3-bf16.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@glennneuber

Copy link
Copy Markdown
Author

Reviewing as consolidator; I wrote H4a and used summarize_lowtemp.py for a live arm today, so both halves land on me.

The rung going first is correct, and the reason given is the right reason.

two arms at different rungs are not comparable on tok/s or req/h at all — KV size drives decode speed — so a table without it invites the one comparison it cannot support.

That is exactly what H4a was for, and the gap you found is real: I wrote the rule that the rung is a result, run_engine_compare.sh refuses to discard it, and then the summarizer that renders repeated arms silently dropped it — with num_ctx sitting unread in its own fixtures. A rule enforced at the runner and lost at the report is not enforced.

a/b ⚠ on distinct values within an arm is the right call rather than collapsing to the first. Repeats of one arm are supposed to sit at one rung; more than one means it escalated between repeats, which is a finding about the model and would otherwise be invisible.

Retiring summarize_lowtemp.py — I agree, with one thing recorded

I used it today to render a bbox_contract arm for MLX/CUDA, so let me confirm the replacement argument holds rather than take it on faith. The three things it did that summarize_reps doesn't:

  • capped-cell exclusion and no result count — these existed because that arm had no ladder. It was a hand-written loop at a fixed window, so "did not finish" was ambiguous between the model cannot and this window was too small. With the ladder always running that ambiguity is gone: a cell finishes at a rung, or the runner says NOT CONVERGED. Agreed.
  • num_ctx columns — subsumed by this PR putting the rung first.

So the replacement is complete for the arm I ran. The only loss is cosmetic: summarize_lowtemp grouped by (model, think) with three values per cell, which reads well for a two-mode comparison. Not worth keeping a second summarizer for, and ADR 0028's whole argument is that a second one is the defect.

Keeping the negative-result document is right — it is the evidence ADR 0029 rests on, and retiring the tool that rendered it does not retire the measurement.

One check I would ask for

summarize_reps.py currently carries #196's mean [lo–hi] fix. If these land in either order the rung column and the interval rendering both need to be present in the merged result — worth confirming they are not two branches from a common ancestor that each drop the other's change.

The negative-result document's status amendment said ADR 0029 "adopts
temperature 0.01 for think-on deliberately". It does not. Both the ADR's own
status line and the code disagree:

  ADR 0029        THINK_TEMPERATURE default is 0; the 0.01 variant was
                  measured and rejected
  sampling.py:92  THINK_TEMPERATURE = float(os.environ.get("THINK_TEMPERATURE", "0"))

Left as written it tells a reader the fork measures think-on at 0.01 when it
measures at 0 — and this document is the cost sheet ADR 0029 cites, so it is
exactly where someone would go to check.

Also states the direction of the error, which matters for reading the table
below: 0 is STRICTER than the 0.01 measured here, so these numbers are a floor
on the cost of the policy, not a ceiling. The two models that lose six of eight
cells at 0.01 do not lose fewer at 0.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@glennneuber
glennneuber merged commit 277fe22 into main Aug 18, 2026
@glennneuber
glennneuber deleted the fix/summarize-reps-reports-the-rung branch August 18, 2026 00:53
glennneuber added a commit that referenced this pull request Aug 18, 2026
#197's rung row scans every section of every run, so the finetext block sets it
off on its own. vision_suite.py:1136 gives finetext `NUM_CTX or 32768` where
every other section takes `NUM_CTX or 16384`, so with NUM_CTX unset the two
differ in every run ever measured — and the qwen3.8 ROCm arms, whose vision
tests all sat at 16384, rendered "16384/32768 ⚠".

A warning that fires on every arm carries no information. The docstring's own
justification is escalation *between repeats*, which is a different axis from
sections *within* a run, and the implementation collapsed both into one list.

Sections that own their window are now excluded from the ⚠ and annotated
instead: "16384 (finetext 32768)". The alarm is left for what the docstring
describes.

Requested-over-served is kept and now has a test. It looks wrong — two pre-#153
finetext blocks record req_num_ctx 32768 against num_ctx 16384 — but those are
exactly the files where `num_ctx` carries the pre-fold defect of reporting the
suite default, so served-first would report a block at a window it did not run
at.

The qwen3.8 ROCm campaign is re-rendered with both rows, and two things it
never disclosed are now on the page. Its finetext arm ran at 32768/4000, not
the "16384 throughout" the prose claimed. And the pooled n=5 think-on arm is
not homogeneous in num_predict — four runs at 2200 and the NUM_PREDICT=4400
ceiling diagnostic at 4400 — which is the one ⚠ that survives scoping. Pooling
those is defensible only because no think-on cell was capped, and the doc now
says so rather than leaving it implicit.

Mutation-tested: unscoping the warning, dropping the annotation, preferring the
served value, and removing the ⚠ entirely each turn the new tests red.

Co-authored-by: Local Dev <local@localhost>
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
glennneuber added a commit that referenced this pull request Aug 18, 2026
…-18 audit (#204)

Eight sites where a label described something other than the field under it.
Recorded on #195 and left open; closed here.

LABELS THAT OVERSTATE WHAT WAS CHECKED

- "✅ all Qs + bbox" gates q1, q2 and q4-bbox. The multi-image prompt asks FOUR
  questions and q3 is never scored, so a cell that answered q3 wrongly still
  read as a clean sweep. Now "✅ q1 + q2 + q4-bbox".
- "name_bbox hits" is a coarse pixel-band match, not an IoU hit; the column now
  says in-band.
- "finetext fabricated" is total_found - sum(recall), which counts every entry
  that matched no gold code -- malformed and duplicate entries included, not
  only invented ones. Now "finetext unmatched", which is what the arithmetic
  supports.

EXPLANATIONS THAT NAME THE WRONG LIMIT

- summarize_contract_matrix's footnote said `cap` meant generation "never
  terminated at that window", attributing to num_ctx a stop that num_predict
  caused. They are different limits and the whole point of the marker is which.
- docs/api.md defined eval_count as "number of tokens in the response". It is
  every generated token, thinking included, and with think+format the total
  across both passes -- normative here as spec R6, asserted by
  TestChatThinkFormatMarkerStop (109 = 100 thinking + 9 content), and the reason
  the vision suite's token column had to stop saying "Answer tok".

FIELDS THAT WERE SIMPLY THE WRONG ONE

- ctx_for read num_ctx where req_num_ctx is the requested window. Files written
  before the num_ctx fold (#153) recorded the SUITE default in num_ctx for the
  finetext block while req_num_ctx kept what was asked, so served-first reports
  those rows at a window their finetext never ran at. Two of the seven committed
  runs change, both from "16384" to "16384/32768 ⚠" -- revealing a mixed window
  that was hidden, not inventing one. Same rule summarize_reps' _window already
  uses, so the two agree.
- summarize_head_to_head's "(?)" marker means "recorded before req_num_ctx
  existed", but it also fired on any cell sourced from the pre-fold
  ft_<tag>.json path, which never wrote that field. It now falls back to
  num_ctx, so the marker identifies what it claims to.
- extbench recorded os.environ["THINK"] verbatim while gen() gates on `!= "on"`,
  so THINK=true wrote "true" into the summary of a run that sent think=False.
  Records both now: think_env and think_on.

Four tests pin the corrected pairings. summarize_lowtemp's "mean IoU (scored
cells)" -- the ninth finding, and the one that inflated a mean by dropping
genuine 0.0 IoUs on a truthiness check -- needs no fix: #197 retired the file.

Co-authored-by: Local Dev <local@localhost>
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant