Repository navigation
Open-rig miss buckets: miss_taxonomy --cities + open_rig_misses_258 (#263 Track 1) - #265
Conversation
…ck 1 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
miss_taxonomy.py gains --cities (default output byte-identical; with the seven original US splits it reproduces the committed #46 pool exactly). open_rig_misses_258.py buckets laurens_mapillary, bayonne, budapest_district5 and juneau at 0.30 and 0.55 with Wilson and pano-bootstrap intervals, near/far, merged separations and the reading rule; laurens_gsv is a recorded gap (no op_cache yet). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… last-digit flip) Python 3.12's compensated sum() and 3.10's naive sum() disagree in the last bits, which flipped two 4-dp null rates. fsum is exact on every version; --check now passes on 3.10, 3.12 and 3.14 with the committed JSON unchanged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
jonfroehlich
left a comment
There was a problem hiding this comment.
Deep review: open-rig miss buckets (#263 Track 1)
Reviewed at f24a3d6. Every number below marked [ran] was measured on this branch, with throwaway scripts that call the PR's own mt.load_rows, orm.load_split, orm.summarize_population and orm.bootstrap on the committed op_caches. Nothing was changed on the branch.
What checks out [ran]:
miss_taxonomy.pywith no--citiesis byte-identical toorigin/main's version, in stdout and in--json-out, at both 0.30 and 0.55. The only differing line isWrote <path>, because I wrote to two different paths.open_rig_misses_258.py --checkpasses on py3.12.tests/test_open_rig_misses_258.pyandtests/test_miss_taxonomy.pypass (40 tests), and CI is green on 3.10 and 3.12. The JSON isi/lf w/lf attr/text eol=lfwith 0 CRs. Thefsumnull mean is the only float sum in the JSON. String seeding ofrandom.Randomis hash-seed independent.- The bootstrap readings hold across 10 other seeds. The reference lower bound is +0.004 to +0.012 on all 10, and GoPro Max reads "neither" on all 10.
- These claims match the data: budapest is 89/89 GoPro Max; morgantown is 95/95 GoPro Max; bayonne is 87/87
GoPro/Max; juneau's scored panos are 92 Trimble MX7 + 2 GoPro Max; the reference is 3 GSV + 4 Mapillary; Paterson has 80 of the 124 merged misses; near-field silent is 104/608 vs 45/1,360; laurens_mapillary has 94 panos, 59 with GT; laurens confidence isunrecorded. - The per-bucket recall-point excesses sum to the total gap (0.2361 vs 0.2362, a rounding difference). Every cell I spot-checked in the doc's five tables matches the JSON.
- The cited figures match their sources: #151 ΔF1 +0.112, #82
all@gopro−0.209, silent_activation 8/62/30, and 1.82–2.13 m giving 15–27%.
Findings, most severe first.
1. High: the headline "neither leads on GoPro Max" depends on Budapest, the split the repo holds out for GT quality
scripts/analysis/open_rig_misses_258.py:68-70 (GROUPS["gopro_max"]), docs/open_rig_misses_258.md:34-42, the docs/README.md row, and the #258 and #263 comments.
HELD_OUT in miss_decomposition.py excludes budapest_district5 for "single-rater GT at low reviewer confidence", and the doc calls it "the split to trust least" (l.167-168). Yet it is one of three members of the pool that carries the headline, contributing 107 of 377 misses and 47 of 151 silent. [ran] Without it (laurens_mapillary + bayonne, 0.30):
| pool | misses | sub_threshold | silent | sub − silent, boot 95% | reading | excess sub / silent | gap |
|---|---|---|---|---|---|---|---|
| 3 GoPro Max (as committed) | 377 | 46.9% | 40.1% | −2.1 to +16.1 | neither leads | +0.128 / +0.116 | 0.236 |
| laurens_mapillary + bayonne | 270 | 52.6% | 38.5% | +2.4 to +26.1 | sub_threshold leads: calibration | +0.178 / +0.127 | 0.284 |
So "the two levers are about the same size" holds only with the low-confidence split included. Without it, calibration is the larger lever by about 0.05 recall points.
Fix: report both pools, and lead with the one that does not include a GT-quality hold-out, or make Budapest a sensitivity row. Say in the Result paragraph that the "neither" reading depends on Budapest. Correct the #258 and #263 summaries to match.
2. Medium: the "silent" bucket is defined wrongly in the doc, and three sentences overclaim from it
The definition is at doc l.21 ("nothing at all, even at the 0.05 floor") and at open_rig_misses_258.py:12. It is inherited from the miss_taxonomy.py docstring.
classify_miss checks the 0.05 floor only inside R. Its annulus test (R to 2R) looks only at preds_kept, the predictions at or above threshold. A ramp with a 0.2 peak at 1.5R is therefore "silent". [ran] At 0.30:
- 57 of 151 GoPro Max silent misses (38%) have a floor peak within 2R, and in 32 of them every such peak is below threshold.
- On the reference it is 75 of 128 (59%).
- Bayonne: 24 of 70.
The definition then drives these sentences:
- l.161, "Half of what Bayonne misses, the model never responds to."
- l.54, "labels or data are the only thing that reaches the silent excess."
- l.107-108, "Those 104 are the population that labels could plausibly reach."
Each states as fact what is an upper bound on single-rater GT. The doc's own caveat at l.194 says so, but only in the Caveats section.
The stricter count supports the near-field reading: [ran] near-field silent misses with no floor peak within 2R are 70 of 608 on GoPro Max (11.5%) against 16 of 1,360 on the reference (1.2%).
Fix:
- Correct the definition: "no floor peak inside R, and no unclaimed above-threshold peak within 2R".
- Add a strict "nothing within 2R" count beside silent.
- Reword the three sentences as upper bounds.
3. Medium: the stated ceiling on what rescoring can recover is the wrong quantity
Doc l.53-54, and the #258 comment ("The verifier's target population on the open rigs is about 0.13 recall points").
Rescoring sub-threshold peaks can recover any sub_threshold miss. The ceiling is therefore the sub_threshold recall points: 0.208 on the GoPro Max pool and 0.281 on laurens_mapillary. The +0.128 excess is the part that would close the gap to the reference, and the reference has its own 0.081 that a verifier would also chase.
Fix: "can recover at most 0.208 recall points on GoPro Max, of which 0.128 is the excess over the reference". Correct #258 to match.
4. Medium: the doc compares cities on all panos, though the benchmark README says to use the unbiased subset for that
benchmark/README.md ("Every city split is stratified, so read the unbiased column too") says the random + empty subset is "the honest between-city comparison", because each split includes 5 hand-picked high-density panos. The doc never mentions the stratification. It also quotes the all-pano recall (bayonne 0.375) against #263's unbiased 0.322. [ran] Dropping the top group from verdicts.json on every split:
- GoPro Max: gap 0.248, sub excess +0.132, silent +0.125, still "neither leads" (−4.6 to +15.2).
- Reference: still calibration, but the lower bound falls to +0.26 points.
- laurens_mapillary: still calibration.
No reading changes, so this is a disclosure fix. Fix: one sentence plus an unbiased row (or columns) for the GoPro Max pool and the reference. Note that the reference's "calibration" verdict is marginal on both subsets.
5. Medium: the doc says #263 fixed the operational test before any number was looked at, but #263 did not define one
Doc l.12-29, and the #258 comment ("fixed in #263 before the run").
#263 fixed only the rule: "silent dominates means labels; sub_threshold dominates means calibration". The operational test (the pano-bootstrap 95% CI of the difference excludes 0) first appears in this PR. A plurality reading of "dominates" would say sub_threshold leads on GoPro Max (46.9 vs 40.1). In a research record, the provenance of a decision rule is part of the result.
Fix: "#263 fixed the rule; this PR operationalizes 'dominates' as …". Say whether that choice was made before the tables were seen, and if it was not, say so.
6. Low: two sentences read as conclusions rather than readings
- l.49, "So the silent excess is not the camera model." The evidence is one split (52 misses, no interval). It is confounded by country and rubric: bayonne and budapest are the two GT regimes where the rubric fought back. It also skips richmond, which has 18 GoPro Max panos (the code comment at
open_rig_misses_258.py:63names only morgantown). Suggest "is not explained by the camera model alone". - l.49-50, "concentrated in bayonne, budapest, laurens_mapillary". Those are all three GoPro Max splits, so this is not a concentration.
The "Which to do first is Jon's call" framing is right.
7. Low: the stated reason recall differs from the scoreboard is wrong
Doc l.148-150 attributes the difference to the fn_confirmed gate and to panos with GT. [ran] fn_confirmed is true on 125/125 budapest and bayonne panos, and on all panos of the four open splits. Panos with no GT add nothing to recall. So 0.510 vs 0.503 (budapest) and 0.375 vs 0.372 (bayonne) come from something else, most likely cache-peak re-matching versus verdict counting. Fix: drop the explanation, or find the real cause.
8. Low: seam handling is not the same across the populations being compared
- Matching. The taxonomy matches without
wrap_x(miss_taxonomy.py:281, and_d2inclassify_miss).score_panohas wrapped by default since #140. [ran] With wrapping, 1 bayonne and 1 juneau miss become hits at 0.30. - Extractor. The 7 reference caches and budapest's predate the
f4c71c8exclude_borderfix. [ran] They carry 0 floor peaks within 10 columns of the edge, against 4 / 39 / 13 for laurens_mapillary / bayonne / juneau. That asymmetry lies between the populations being compared. The reference seam-strip GT is richmond 1 silent, bend 1 silent, paterson 1 silent + 2 merged.
The effect is at most ~0.002 recall points and changes no reading. It deserves one caveat line beside the reference row; rampnet1_findings.md already says "committed op_cache files predate the extractor fix".
9. Low: the negative merged excess comes almost entirely from Paterson
Doc l.42. [ran] Without paterson, the GoPro Max merged excess is +0.008, not −0.026. Sub / silent become +0.118 / +0.109 and the gap 0.254. The doc notes the 80 of 124 (l.74) but does not connect it to "Merged misses go the other way". Add half a sentence.
10. Low: the distance caveat leans one way and sits away from its table
Doc l.94 and l.200-205. "The near-field counts above are, if anything, low" applies to the reference too. docs/da3_calibration_101.md puts paterson 2025 + gainesville 2026 at about 1.8 m, clovis at 2.02 m, annapolis at 2.12 m, and morgantown is GoPro Max. So the 2.5 m assumption biases both sides, and the direction of the near-field contrast is not established by it. Fix: one line under the near/far table that points to the caveat and says the bias is on both sides.
11. Low: the laurens_gsv fill path is not replicable as written
open_rig_misses_258_gpu.sh:3-5says "Run on makelab2 (A40) on 2026-10-20; writes analysis_out/op_cache/laurens_gsv.json (committed)". The PR body says the run was not confirmed and nothing was committed. The header should say that.- The repro step prints a count but never gates. With
set -uand no-e,extractruns andALL_STEPS_DONEprints whatever repro finds. Make itexit 1on a mismatch, or say plainly in the log that it does not gate. - There is no parity step.
juneau_254_gpu.shrunslow_floor_sweep.py parity --cities juneau. Doc l.221 says "tp/fp/fn at 0.55 must equal the scoreboard" but names no command, and per finding 7 the cache-derived recall does not equal the scoreboard exactly on two splits. Name the command and the tolerance. - The doc does not say how to obtain
benchmark/laurens_gsv/panosandbenchmark/laurens_mapillary/panos. Point toimagery_manifest.jsonand the fetch path. - "record the paid:false usage row" does not name a tool. Say which (
slurm_usage.pyif on klone, or the row format if on makelab2).
12. Low: the "about evenly" claim rests on two point estimates with no interval
+0.128 vs +0.116 are point estimates. A pano bootstrap of (sub excess − silent excess), stratified like the existing one, would say whether "about evenly" is distinguishable from either lever being larger. That matters more now, given finding 1.
Nits
- l.37 and l.160 call bayonne "a tie", while l.193 says the "neither leads" readings are "mostly underpowered, not ties". Pick one framing. Bayonne's point estimates are close (47.4 vs 46.1), so "close, and underpowered" is accurate.
- l.168, "The brief for this work called Budapest…": a reader of the doc will not know which brief this is. Cite the source, or drop it, since the records settle the question.
miss_taxonomy.py --cities ""gives an empty tuple and prints "all held out".n_poolcounts the names given, not the caches found. Neither matters today.- The doc's tables are transcribed by hand and nothing checks them. I spot-checked them against the JSON and found no mismatch.
🤖 Generated with Claude Code — Opus 5.5, claude-opus-5-5
…biased subset, generated tables
1. GoPro Max pool is laurens_mapillary + bayonne; budapest_district5 (HELD_OUT)
is its own row. Headline, decomposition and comments recomputed.
2. strict_silent (no floor peak within 2R) beside silent; silent definition fixed.
3. Rescoring ceiling is all sub_threshold recall points, not the excess.
4. Unbiased subset (verdicts group != top) for every between-city table.
5. Provenance of the bootstrap test stated as it happened.
6. Morgantown hedged. 7. Real cause of the README recall difference.
8. Seam note. 9. Reference without paterson. 10. Camera height beside the table.
11. GPU script: status header, set -euo pipefail, manifest/repro/parity/score
gates, ledger row via rampnet.ledger.append_rows.
12. Pano-bootstrap intervals on every recall-point excess and on
(sub excess - silent excess).
Nits: --cities '' errors; n_pool counts loaded splits; doc tables are
generated from the JSON and --check verifies them.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
jonfroehlich
left a comment
There was a problem hiding this comment.
Re-review at 543d6a6
I re-checked every finding from the first review at 543d6a6. [ran] means I measured it on this commit. The independent checks use throwaway scripts that read the committed caches, verdicts and records directly, not the PR's helpers.
What I ran:
open_rig_misses_258.py --checkpasses on Python 3.10 (uv, numpy only), 3.11 and 3.12. It now covers the JSON and all 10 generated doc blocks.tests/test_open_rig_misses_258.pyandtests/test_miss_taxonomy.pypass (45 tests).miss_taxonomy.pywith no--citiesis still byte-identical toorigin/main, in stdout and in--json-out, at 0.30 and at 0.55.- The new pool numbers match my own count:
- laurens_mapillary + bayonne: 142 / 270 sub_threshold, 104 / 270 silent.
- Excess +0.178 sub, +0.127 silent; rescoring ceiling 0.258.
- The strict-silent counts match my own count. Strict silent is 23 / 46 / 25 / 12 per split, and 53 on the reference. Near field it is 51 / 407 on the pool against 16 / 1,360 on the reference.
- The seam-strip ramps on the reference are as stated (5 ramps, all missed).
I found no per-finding reply as a comment in the PR thread. The PR body was rewritten instead, and I read that.
| # | Finding | Status |
|---|---|---|
| 1 | Headline depended on pooling Budapest | Fixed [ran]. GROUPS["gopro_max"] is now laurens_mapillary + bayonne, and test_budapest_is_never_pooled pins it. Budapest has its own row. The doc says plainly that the earlier reading is withdrawn. |
| 2 | "silent" defined wrongly; overclaims built on it | Fixed [ran]. The definition is correct in the doc (l.24-26) and the docstring. The strict-silent column is computed correctly; counts above. "Half of what Bayonne misses…" and "the only thing that reaches" are gone. |
| 3 | Rescoring ceiling was the wrong quantity | Fixed. 0.258, of which 0.178 is the excess; the doc says so, and so does #258. |
| 4 | Stratified sampling ignored | Fixed [ran]. The unbiased subset is used for the between-city tables, and the reference's marginal verdict is called out. |
| 5 | Provenance of the reading-rule test | Fixed. l.44-49 says when the test was chosen and what had already been seen. That is the right disclosure. |
| 6 | Morgantown and "concentrated" overclaims | Fixed. Softened, with the confounds and richmond's 18 GoPro Max panos named. |
| 7 | Wrong explanation of the scoreboard recall gap | Fixed [ran]. The new explanation is exactly right. Bayonne has one detection judged False within R of a confident missed mark, and budapest has two judged unsure (112 + 1 and 151 + 2 against verdict-True counts). The taxonomy's hits at 0.55 equal scoreboard.json tp on all four open splits. Bend / paterson / gainesville are 2 / 2 / 7 short, as the doc says. |
| 8 | Seam handling differs across populations | Fixed. The caveat is in the doc, with the measured counts. |
| 9 | Merged excess is a Paterson artifact | Fixed. There is now a no-Paterson reference and an excess table against it. |
| 10 | Distance caveat leaned one way | Fixed. The caveat now sits under the near/far table and covers both sides. |
| 11 | laurens_gsv fill path not replicable | Mostly fixed, one new defect (A below). The STATUS header is accurate. The run has set -euo pipefail. The manifest-verify, repro-equality and parity steps all exist. The pano source is named. [ran] The score step's logic reproduces the scoreboard exactly on juneau (204 / 7 / 93) and laurens_mapillary, and low_floor_sweep.py parity --cities juneau passes. |
| 12 | No interval on the decomposition | Fixed. Every excess carries a pano-bootstrap interval, and sub − silent is +0.051 (−0.008 to +0.109). |
| nits | Fixed: "tie", "the brief", --cities "" (now an error, with a test), n_pool (now counts splits actually loaded), and the doc tables (now generated and checked). |
The issue comments say what the doc says. The edited #258, #263 and #158 comments, and their follow-ups, match the doc on the pool, the shares, the bootstrap, the ceiling, the excess interval, strict silent and the provenance. Superseded text is struck through rather than deleted, which is the right way to do it.
New
A. Medium: a score-gate failure stops the run before the cost is recorded
open_rig_misses_258_gpu.sh:25-26, 98-139. Under set -e, a score mismatch exits before the ledger step, so the extract's GPU time is never recorded. The script's own header expects a mismatch on GSV ("bend's committed cache gives tp 247 / fp 5 against the scoreboard's 249 / 10; paterson and gainesville are 2 and 7 TPs short"), and laurens_gsv is a GSV split. So the likeliest outcome of a real run is a mismatch that skips the row.
That is the same failure #139 / #143 turned into a standing rule: a ledger row has to outlive the run.
[ran] Even a non-GSV split can trip the gate. The script's own score_pano call wraps the x axis, and on bayonne it gives tp 114 / fp 25 against the scoreboard's 113 / 23.
Fix: move ledger to run right after extract, before parity and score, or write the row from a trap ... EXIT whenever the extract times file exists. Optionally default LEDGER through rampnet.ledger.canonical_repo_root, so a worktree run lands in the main checkout without the env var.
B. Medium: on a clone with autocrlf=true, --check fails because the doc is not LF-pinned
.gitattributes pins analysis_out/open_rig_misses_258.json but not docs/open_rig_misses_258.md. BLOCK_RE matches -->\n, and the doc is read with newline="".
[ran] With a CRLF copy of the doc, --check --doc <crlf copy> exits 1 with doc is missing generated blocks [all 10]. A default Git-for-Windows install (core.autocrlf=true) gets exactly this, and so does test_committed_report_rederives_byte_for_byte. The message points at the doc's content, not its line endings. Jon's checkout is not affected, because autocrlf=input. CI is not affected either, because it runs on Linux.
Fix: add docs/open_rig_misses_258.md text eol=lf. The same pattern pins other checked markdown, e.g. analysis_out/recall_by_depth_112.md (.gitattributes:89; the #101 tables, line 86, were pinned for exactly this failure in the #203 review). Optionally make BLOCK_RE accept \r?\n, or have the error mention line endings.
C. Nits
- Mixed units in the excess cells. Each excess cell mixes a fraction point estimate with an interval in percentage points, e.g.
0.258 (+0.178; +13.6 to +22.0). l.132-133 says so, but the prose uses 0.229 to 0.333 for the same kind of interval. One unit throughout would read more easily. - Minus signs. The generated tables print an ASCII hyphen-minus (
-13.6), while the prose uses U+2212. - Morgantown's excess interval is conservative.
bootstrap_excessresamples morgantown independently of the reference that contains it, so it ignores their positive covariance. - "+0.0 to +22.0 · neither leads" (unbiased, reference without Paterson) reads as a contradiction at one decimal. The lower bound is ≤ 0 before rounding. Printing two decimals, or "≤ 0", would avoid it.
Verdict
NEEDS FIXES. The fixes are small: A (step order) and B (one .gitattributes line). The analysis, the doc and the issue comments are sound at 543d6a6. Nothing in A or B changes a committed number.
🤖 Generated with Claude Code — Opus 5.5, claude-opus-5-5
…able units A. open_rig_misses_258_gpu.sh: the ledger row is written right after extract, before parity/score, and an EXIT trap records an extract that died part-way as status failed; LEDGER defaults to the main checkout via rampnet.ledger.canonical_repo_root. score now reports instead of aborting (it uses score_pano's wrap_x=True, the scoreboard's convention; cache-vs- records differences such as bayonne's 3 seam peaks and GSV drift are expected), and parity is the gate. Both paths exercised with a stub. B. docs/open_rig_misses_258.md pinned eol=lf; fill_doc names CRLF as the cause when no block matches. C. Excess cells in one unit (recall-point fractions), U+2212 minus, exact-zero bounds unsigned with the rule stated, morgantown covariance noted. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Replies to the two reviewsBoth reviews are covered here: the first at f24a3d6, fixed in 543d6a6, and the re-review at 543d6a6 (pullrequestreview-5591791903), fixed in 4fa23c1. Last time I rewrote the PR body instead of replying here; this comment is the per-finding record. First review (fixed in 543d6a6)
Re-review (fixed in 4fa23c1)A. Ledger lost on a gate failure.
Seam / gate rule.
B. CRLF clone breaks
C. Nits.
No committed number changed. 🤖 Generated with Claude Code — Opus 5.5, claude-opus-5-5 |
jonfroehlich
left a comment
There was a problem hiding this comment.
Final re-review at 4fa23c1
I checked out 4fa23c1 into a fresh detached worktree and ran everything below there. Your per-finding reply (#issuecomment-6303890331) matches what I found.
A, a gate failure losing the ledger row: fixed. I tested the GPU script with a stub PY that fakes the manifest, extract and parity steps and runs the real interpreter for everything else. Each case wrote to a scratch ledger:
| case | exit | ledger |
|---|---|---|
| extract dies part-way | 3 | 1 row, status: "failed", elapsed_s set, panos_scored: null (written by the EXIT trap) |
| extract ok, parity fails | 1 | 1 row, status: "ok", no duplicate from the trap |
| everything passes | 0 | 1 row, ok; score prints DIFFERS … (parity is the gate) and does not abort |
The canonical_repo_root default for LEDGER resolves. The wc -w comparison still holds if wc pads its output with spaces. The worktree was clean after each run.
B, CRLF clone breaks --check: fixed. git ls-files --eol reports the doc as attr/text eol=lf. On a CRLF copy, --check now says why it failed ("the doc has CRLF line endings; it is pinned eol=lf…").
C, nits: fixed.
- The excess cells use one unit, fractions, for both the point and the interval.
- The tables print U+2212 minus.
- Exact-zero bounds print unsigned, and the rule is stated in the doc. The one
0.0 to +22.0 · neither leadscell is a true 0.0: the raw bootstrap bound is0.0. - The morgantown covariance note is in the doc in both places.
Nothing regressed:
--checkpasses on Python 3.10 (uv) and 3.12.tests/test_open_rig_misses_258.pyandtests/test_miss_taxonomy.pypass (45).miss_taxonomy.pywith no--citiesis still byte-identical toorigin/mainat 0.30 and 0.55.- The JSON is unchanged since
543d6a6. - CI is green on 3.10, 3.12 and checks.
One cosmetic nit, not blocking. A bound that rounds to zero from below prints −0.000, for example the morgantown unbiased strict-silent excess. That is the true sign, so leave it unless it bothers you.
READY
🤖 Generated with Claude Code — Opus 5.5, claude-opus-5-5
…U step The d74d680 launch on makelab2 (torch 2.13 cu130) did complete: its laurens_mapillary repro kept all 374 peak positions, but 88 of 94 panos differed in score by up to 5.4e-5, so the exact-equality gate would stop every run on that env. The gate now requires identical pano set, peak counts and positions, |score diff| <= 1e-3 and no crossing at 0.30/0.55. The repro step's GPU time now gets its own ledger row. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…Track 1) 9f4b7ce on the A40: manifest OK, laurens_mapillary repro within tolerance, extract 86 panos in 164.6 s, then low_floor_sweep parity FAILED on the count arm (records 121 vs cache 111 at 0.55; every displacement < 0.44 R). All 18 records-only detections have a cache peak within 0.44 R scoring 0.38-0.54: the deployed-vs-re-extraction score gap #151 observed. The cache is kept as analysis_out/open_rig_258/laurens_gsv_candidate_op_cache.json, read by nothing, pending a decision on a parity exception; laurens_gsv stays out of the tables and miss_taxonomy's default output is unchanged. Usage rows: this run's repro + extract, and the superseded d74d680 launch's two (elapsed from log stamps), appended to the main checkout ledger first. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
laurens_gsv: the extract ran, failed the parity gate, and is not in the tablesThe GPU fill path ran on makelab2 (A40) on 2026-10-20 at Two script changes before the run (
|
…gsv (#263 Track 1) The cache from the 9f4b7ce makelab2 run moves to analysis_out/op_cache/laurens_gsv.json with a low_floor_sweep.PARITY_EXCEPTIONS entry on the sao_paulo precedent: count arm only (111 cache vs 121 records detections at 0.55), every displacement < 0.44 R, the same peaks scored lower than the deployed GSV run (the #151 deployed-vs-re-extraction drift). It reproduces #25's r2048 re-extraction of laurens_gsv exactly. open_rig_misses_258: laurens_gsv joins the tables (held out, with the under-scoring caveat) and a generated 'One town, two rigs' table: at 0.30 silent 0.137 vs 0.123, sub_threshold 0.281 vs 0.164 recall points (GoPro Max vs GSV, single rater). Regenerated because the cache now exists, each check passing: - scoreboard: laurens_gsv RampNet AP from op_cache (0.768 vs bundle 0.494); tp/fp 112/8 unchanged (records-based). - input_res_sweep_25: instrument check now covers laurens_gsv (SHA256SUMS updated). - laurens_paired_151: rampnet@0.30 gains its GSV side (= r2048 row); doc tables, dated note, rows_sha256 pin. - eval_protocol_150 reproduction.json and cascade_cost_35 summary.json (gaps). Tests that asserted 'laurens_gsv has no op_cache' are updated to the new state. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Addition: laurens_gsv admitted as a parity exception, and a same-town readOn Jon's go-ahead, the laurens_gsv cache that failed the count arm of the parity gate (comment above) is admitted on the sao_paulo precedent. What changed:
laurens_gsv in the tables (held out, all panos):
Caveat, in the doc beside the row: the cache under-scores the deployed run. Recall at 0.55 is 0.459 from the cache, against the scoreboard's records-based 0.509. So the row's sub_threshold / silent split is conservative on the sub_threshold side. One town, two rigs (new section; single rater, whole-arm, not paired). In recall points at 0.30:
The silent rate is close on the two rigs, and the gap is in sub_threshold. That points at calibration as the Laurens rig effect. The GSV cache's under-scoring would, if anything, understate that contrast. What else adding the cache moved. Each of these is regenerated, and each check passes:
Commit 🤖 Generated with Claude Code — Opus 5.5, claude-opus-5-5 |
jonfroehlich
left a comment
There was a problem hiding this comment.
Re-review at c079b76 (laurens_gsv cache admitted)
I checked out c079b76 into a fresh detached worktree and ran everything below there.
What holds up [ran]:
- The parity-exception diagnosis matches the cache. I recomputed it from the cache and
records.jsonlwithout going throughlow_floor_sweep:- 121 records detections against 111 in the cache at 0.55.
- All 18 records-only detections have a cache peak within 0.439 R, scoring 0.381–0.54.
- The maximum displacement is 0.439 R.
- Matched peaks score lower in the cache 70 times and higher 51 times.
low_floor_sweep.py parity --cities laurens_gsv,sao_pauloprints both as ratified exceptions and exits 0.
- The #25 claim holds. Against
input_res_sweep_25/cache/r2048/laurens_gsv.json, the cache has the same peaks on all 86 panos. The maximum score difference is 3.98e-5, and the maximum position difference is 5e-7, which is the r2048 file's 6-dp rounding. The cache sha256 startsaf95b501, as the doc says. - The scoreboard's tp/fp did not move. The laurens_gsv RampNet cell is still tp 112 / fp 8 / fn 108, with P/R/F1 unchanged. Only
ap(0.494 → 0.768499) andap_sourcechanged. - No other number moved. I diffed
scoreboard.json,eval_protocol_150/reproduction.json,input_res_sweep_25/instrument_check.json,cascade_cost_35/summary.jsonandlaurens_paired_151.jsonfield by field against4fa23c1. Every change is a laurens_gsv addition, except the movedrows_sha256. Inlaurens_paired_151, no arm row was removed and 86 laurens_gsv rows were added. Thegtrows only gain arampnet@0.30hit key.inputs_sha256only gains the new cache. No existing two_by_two row changed. Inreproduction.json, 13 cells now take their AP from the op_cache. scripts/check_all.py --all: 23 passed, 0 failed, 2 skipped. The skips are the two local-cache entries.benchmark_power_135passed (541 s). The tree was clean afterwards.- Tests: 447 passed across
test_open_rig_misses_258,test_miss_taxonomy,test_scoreboard,test_laurens_paired_151,test_input_res_sweep_25,test_eval_protocol_150,test_low_floor_sweep,test_cascade_*,test_slurm_usageandtest_vertex_usage. - The four updated tests are honest re-pins, not loosened assertions.
test_input_res_sweep_25keeps the extras total (173) and the at-seam assertion.test_scoreboardnow also pins tp/fp at 112/8.test_laurens_paired_151asserts the rampnet@0.30 GSV side equals rampnet_r2048@0.30 cell for cell, which is stronger than before.
open_rig_misses_258.py --checkis byte-exact on Python 3.10 (uv) and 3.12.- The four usage rows are well-formed. Each parses with
rampnet.ledger.read_rows, each has a distinctrun_idkey, and none collides with an existing row, so all four count inledger_totals. Theirelapsed_sandtsmatch the stamps in both committed logs (91 s / 167 s superseded; 89.5 s / 164.6 s current). - The doc's under-scoring caveat reads correctly. The cache scores deployed hits lower, so it inflates GSV sub_threshold, and the two-rig sub_threshold contrast is understated. The doc says exactly that.
NEEDS FIXES
1. Medium [ran]: the ledger still drops a completed step's GPU time in two failure paths
open_rig_misses_258_gpu.sh, the on_exit trap and the ledger step. Rows are written only at the ledger step. The trap records only a step whose times file holds one timestamp, meaning it started and never finished. A step that finished before the run died is in neither path. I ran a stub PY against scratch ledgers:
| case | exit | rows written |
|---|---|---|
| repro extract finishes, repro tolerance gate fails | 1 | none (no ledger file) |
| repro ok, laurens_gsv extract dies | 3 | extract: failed only; the finished repro's time is lost |
| extract ok, parity fails | 1 | repro: ok, extract: ok |
| all pass | 0 | repro: ok, extract: ok |
The first case is the repro gate's whole reason to exist, and it records nothing. This is the same #139 / #143 rule as the last round's finding A. The real makelab2 run hit neither path, so the committed ledger is complete.
Fix: write each step's row as soon as that step's GPU work finishes, before its gate. Alternatively, have the trap write ok for any two-timestamp step not yet ledgered, and keep a done-set so nothing is written twice.
2. Low: the Cost paragraph contradicts the doc's header
docs/open_rig_misses_258.md, Cost section: "The laurens_gsv extract … is not used by any number either". That is false. Every laurens_gsv row, the "One town, two rigs" table and its prose come from that cache. The header correctly says every number is CPU-only "except the laurens_gsv cache".
3. Low: four places still say laurens_gsv has no op_cache
scripts/analysis/laurens_paired_151.py:265, and so the committedlaurens_paired_151.jsonlegs note, still reads"analysis_out/op_cache (laurens_gsv has none)", besidearms: ["laurens_gsv", "laurens_mapillary"].docs/eval_protocol_150.md:143-144says "the 11 RampNet cells whoseap_sourceis the op_cache (every split butlaurens_gsv, which has no op_cache …)".reproduction.jsonnow has 13. Bayonne and Juneau each got a dated note at l.259 and l.270, and laurens_gsv has none.docs/model_comparison.md:836-839says "laurens_gsvdoes not [have a 0.05-floor op_cache]". The AP shown in that generated block (0.494) is still the bundle AP, as for every split, so only the sentence is stale.docs/juneau_split_254.md:196(optional) says the two figure plotters "exit on" laurens_gsv. That blocker is gone, so the figures could now be regenerated.
Nit. In the Gaps section, "What adding the cache changed elsewhere. Nothing else moved:" is followed by a list of what moved. Something like "These moved, and nothing else did" would read correctly.
Fixes 1 to 3 are each a few lines and change no committed number. With them, READY.
NEEDS FIXES
🤖 Generated with Claude Code — Opus 5.5, claude-opus-5-5
…ale laurens_gsv text 1. open_rig_misses_258_gpu.sh writes each GPU step's row the moment its GPU work finishes, before its own gate and any later step, with a done-set so the EXIT trap only adds what is missing. Exercised with the reviewer's stub harness: repro gate fails -> repro ok; extract dies -> repro ok + extract failed; parity fails -> both ok; all ok -> both ok; ledger off -> no file. 2. Cost paragraph: the laurens_gsv cache is a GPU product every laurens_gsv number reads. 3. Stale 'laurens_gsv has no op_cache': laurens_paired_151 leg note (rows_sha256 pin moves to 6a94d27b), eval_protocol_150.md (13 op_cache cells, dated note), model_comparison.md and juneau_split_254.md dated notes. Nit: 'Nothing else moved' header reworded. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
eval_protocol_150.md: keep both dated notes; the laurens_gsv note quotes the post-merge counts (224 cells, 13 op_cache cells). All checks re-run clean. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Conflicts resolved: - usage_log.jsonl: main's file plus this branch's four open-rig-258 rows. - laurens_paired_151: rebuilt from main's JSON with its curb probe; now carries both the Vistas row and the laurens_gsv GSV side (rows_sha256 6b1376d1, pin updated). - docs/laurens_paired_151.md, docs/eval_protocol_150.md: both dated notes kept; ours quotes the post-merge counts (233 cells, 13 op_cache cells). check_all --ci and the affected tests pass. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ens_gsv gap row) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Reply to the re-review at c079b76 (pullrequestreview-5593973293)The fixes are in
Finding 1, the five stub cases:
Finding 3, the four places:
The #267 merge. It conflicted in
Checks after the merges. 🤖 Generated with Claude Code — Opus 5.5, claude-opus-5-5 |
jonfroehlich
left a comment
There was a problem hiding this comment.
Re-review at af646f2
I reviewed this in a fresh detached worktree at af646f2. Its merge-base is origin/main (31de31a).
The previous round's findings are fixed [ran].
- Ledger rows per step. I ran a stub
PYagainst scratch ledgers, with the five cases from your reply plus one more:
| case | exit | rows |
|---|---|---|
| repro tolerance gate fails | 1 | repro: ok |
| extract dies after repro | 3 | repro: ok, extract: failed |
| parity fails | 1 | repro: ok, extract: ok |
| all pass | 0 | repro: ok, extract: ok |
STEPS without ledger |
0 | none (no ledger file) |
| repro's own extract dies (extra case) | 4 | repro: failed |
No case wrote a row twice. The tree and the cache (af95b501…) were unchanged after every run.
- Cost paragraph. It now reads correctly.
- Stale "no op_cache" text. It is fixed in the four places I named:
laurens_paired_151.pyand its leg note,eval_protocol_150.md(13 cells, plus a dated note),model_comparison.md, andjuneau_split_254.md.
The nit is fixed too.
The merge resolutions are correct [ran].
usage_log.jsonl: its first 294 lines are byte-identical toorigin/main's. The 4 rows after them are byte-identical to the open-rig rows atc079b76.laurens_paired_151.jsonagainstorigin/main: every difference is a laurens_gsv addition.- 86 laurens_gsv
scoresrows were added and none was removed. - The 469
gtrows differ only by the addedrampnet@0.30hit key. - The only leg that changed is
rampnet@0.30: its arms gained laurens_gsv and its note was updated. The Vistas leg is main's, unchanged. inputs_sha256only gains the cache. two_by_two only gains the tworampnet@0.30keys.- The only
tables.scoresrow that changed isrampnet@0.30. rows_sha256is6b1376d1…, andlaurens_paired_151.py --checkre-derives it.
- 86 laurens_gsv
scoreboard.json,reproduction.json,instrument_check.jsonandcascade_cost_35/summary.jsonagainstorigin/main: a field-by-field diff shows only laurens_gsv changes:- The scoreboard changes only laurens_gsv's AP (0.493735 → 0.768499) and
ap_source; tp/fp stay 112/8. - The cascade summary adds laurens_gsv gap rows, one per challenger, the Vistas 1024 row included.
- No pre-existing number moved.
- The scoreboard changes only laurens_gsv's AP (0.493735 → 0.768499) and
eval_protocol_150.py --check: 233/233 scoreboard cells and 30/30 benchmark_eval cells equal, 0 differing, 0 unchecked.scoreboard.py --check:scoreboard.jsonandmodel_comparison.mdare current.
Checks [ran].
open_rig_misses_258.py --checkis byte-exact on Python 3.10 (uv) and 3.12.scripts/check_all.py --ci: 22 passed, 0 failed, 3 skipped (local-cache).- 322 tests passed across the open-rig, taxonomy, paired-151, scoreboard, input-res-25, eval-protocol-150, low-floor and cascade-cost test files.
One stale sentence came in with the #267 merge (Low). docs/vistas_transfer_126.md:868-870 still says summary.json lists "eight of the nine" new Vistas pairs as gaps, and that "the ninth, laurens_gsv, has no op_cache, so it cannot enter the cascade at all". After af646f2, summary.json lists the Vistas laurens_gsv pair as "published detections exist but this pair was not run", like the other eight. The fix is a one-line dated note, like the ones in cascade_cost_35.md and model_comparison.md. It moves no number, and summary.json itself is right, so I am not holding the PR on it.
READY
🤖 Generated with Claude Code — Opus 5.5, claude-opus-5-5
…eption) op_cache Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
One more stale line, from the #267 merge, is fixed in 🤖 Generated with Claude Code — Opus 5.5, claude-opus-5-5 |
Track 1 of #263: bucket RampNet's misses on the open-imagery splits with the #46 taxonomy, to see whether the open-rig recall gap is calibration (sub_threshold) or labels (silent). Revised after review (findings 1 to 12 and the nits).
What changed
scripts/analysis/miss_taxonomy.pygains--cities(comma- or space-separated; pools the named splits that are inUS_SPLITS; unknown or empty names are an error). With no flag, stdout and--json-outare byte-identical tomain.--threshold 0.55already worked.scripts/analysis/open_rig_misses_258.py(new, CPU,--check):HELD_OUT(low-confidence single-rater GT), so it gets its own row and is never pooled. Also morgantown (GoPro Max inside the reference), the Qualitative failure taxonomy: bucket each model's FP/FN from cached detections #46 reference (7 US splits) and the reference without paterson.group != "top").records.jsonl.analysis_out/open_rig_misses_258.json(LF-pinned) and generates every table in the doc betweenBEGIN/END GENERATEDmarkers.--checkfails if either the JSON or a doc table drifts.docs/open_rig_misses_258.md. In order: corrected bucket definitions; the reading rule with a plain account of when the bootstrap test was chosen; which table each reading uses; the result; the generated tables; the per-rig reading; caveats (seam, GT anchoring, single rater, camera height); gaps; and the reproduce commands. A row is added todocs/README.md.scripts/analysis/open_rig_misses_258_gpu.sh: the laurens_gsv extract. Its header now says it has not been run to completion. It runs underset -euo pipefail, with gates in this order:imagery_manifest.py --verifylow_floor_sweep.py parityscoreboard.json(exits 1 on any difference)paid: falserow appended to$LEDGERviarampnet.ledger.append_rowstests/test_open_rig_misses_258.py:--citiesreproduces the committed Qualitative failure taxonomy: bucket each model's FP/FN from cached detections #46 pool exactly (124 / 166 / 9 / 128 of 427).--checkpasses, which covers the doc tables.Result (0.30, all panos; the unbiased subset gives the same readings)
Not done
silent_activation.py, GPU), so even strict silent remains an upper bound.No GPU or paid model was used for any committed number, so there is no usage-log row.
Refs #263, #258, #46, #158.
🤖 Generated with Claude Code — Opus 5.5, claude-opus-5-5