Skip to content

Add Laurens, IA — the first rural split (recall 0.390) and register it as the 8th US split - #152

Open
jonfroehlich wants to merge 4 commits into
mainfrom
benchmark/laurens
Open

Add Laurens, IA — the first rural split (recall 0.390) and register it as the 8th US split#152
jonfroehlich wants to merge 4 commits into
mainfrom
benchmark/laurens

Conversation

@jonfroehlich

Copy link
Copy Markdown
Member

Adds Laurens, Iowa — the first rural split — and works it through docs/adding_a_benchmark_city.md. Companion to ProjectSidewalk/sidewalk-auto-labeler#48; findings in #149 and #151.

Why this split exists (Phase 0)

Rural / small-town streetscape on a consumer 360 rig. Every existing split is urban, suburban or a college town; Laurens is 1.91 km² and 1,264 people. Pooled — US deployment city, complete verdict-grade GT.

The result

Precision 0.898  (CI 0.827-0.942)    unbiased 0.863
Recall    0.390  (CI 0.331-0.451)    unbiased 0.325

Recall is roughly half the next-worst split (clovis 0.650). It is not a weak or ramp-poor sample: Laurens is ramp-rich (2.65 ramps/pano, 3rd of 10), misses/pano 1.62 vs 0.45–1.19 elsewhere, and — uniquely among all ten splits — its misses are near, not far-field (delta +0.004 in normalized y; every other split is −0.013 to −0.030). The reviewer's leaf-litter reading did not survive measurement. Full analysis and the discriminating experiment: #151.

Parity gate: 111/111, 100.0% exact, med R 0.000 — bit-exact, as a Mapillary split should be.

Pooled numbers move

thr P before → after R before → after
0.30 0.9002 → 0.8991 0.7927 → 0.7640
0.55 0.9637 → 0.9594 0.7223 → 0.6864

n 859 → 953 panos. Precision untouched; the recall drop is Laurens' own 0.390 entering the pool.

Two published studies pinned

farfield_forensics.py and silent_activation.py read the live US_SPLITS but are frozen studies — their tests pin the committed result JSONs to numbers quoted in the issues' §0c. Registering a city silently restated both published findings. Each now carries its own explicit split tuple with the reason. This is a latent trap for every future split, not something specific to Laurens, and is probably worth a line in the runbook.

Checklist

Phase 1 — bundle

  • City detected (both sources; area hash d8dd392b…)
  • export_benchmark.py --bundle exited zero; index.csv written, zero decay
  • records.jsonl committed; panos/ archived durably on makelab2 (8.3 GB Mapillary + 40 GB GSV, both reconciled 1:1, zero decay)
  • Strata 5 / 64 / 25 = 94, not 5/95/25 — geometric, not a sampling artifact: _spread walks the whole pool and the town saturates at 64 detection panos held 30 m apart. Unbiased column is 89 vs the usual 120, so intervals are wider. Strata definitions and de-clustering unchanged.

Phase 2–3 — ground truth

  • Reviewed with gt_gallery.py at model resolution
  • review_notes written · [x] verdicts.json committed · [x] score_validation.py run, unbiased recorded
  • Camera provenance present (GoPro / GoPro Max, all 94)
  • imagery_manifest.json written before review (7485886ea08f671b) — a step the runbook still does not mention

Phase 4 — operating point

Phase 5 — code

  • US_SPLITS / CITY_SPLITS / ALL_SPLITS · [x] TIER · [x] BENCHMARK_SPLITS · [x] SPLIT_HEADER
  • HELD_OUT — n/a, pooled
  • tier_of() already resolves GoPro Max → action-modern
  • SERIES slot 7 (#4a3aa7), vacated by the manual_gold fold — no invented hue
  • pytest -q — 8 failures in test_scoreboard.py, deliberately not silenced (see below)

Phase 6 — docs

The decision this needs

No challenger model has been scored on Laurens. Consequences, all real rather than cosmetic:

  • Every challenger becomes partial coverage (7/8) and drops out of the pooled tables — arguably correct behaviour, but it guts the model comparison until they are re-run.
  • test_ap_ordering_is_not_an_artifact_of_the_floor now compares RampNet's 8-split AP (0.829) against YOLO's 7-split AP (0.730) — margin 0.0993 against an asserted 0.1. That is a population mismatch, not a stale constant, and bumping the threshold would paper over exactly the artifact the test exists to catch.

Two ways out — your call, which is why the constants are untouched:

  1. Re-run the challengers on Laurens. Correct and expensive (~15 models, GPU + API).
  2. Keep Laurens out of the scoreboard's comparison basis until they catch up, while leaving it pooled in the operating-point analysis. Cheap, but the scoreboard and the operating point then quote different populations, which needs saying loudly in the docs.

🤖 Generated with Claude Code (claude-opus-5[1m], effort: high)

jonfroehlich and others added 4 commits August 31, 2026 10:46
Phase 0 intent: rural / small-town streetscape on a consumer 360 rig.
Every existing split is urban, suburban, or a college town; Laurens is
1.91 km2 and 1,264 people. It answers whether the operating point holds
where sidewalks are intermittent and driveway aprons outnumber ramps.

Imagery: Mapillary, GoPro Max 5760x2880, all captured 2025-11 by a single
uploader in 19 sequences — the most uniform vintage of any split.
Detection run: 4,495 panos, 9.3% with an operational detection.

Strata are 5 top / 64 random / 25 empty = 94, not the standard 5/95/25.
The shortfall is geometric, not a sampling artifact: _spread walks the
whole candidate pool greedily, and the town saturates at 64 detection
panos held 30 m apart. The unbiased column (random + empty) is therefore
89 panos against the usual 120, so its intervals are correspondingly
wider. Strata definitions and the de-clustering rule are unchanged.

111 operational detections to adjudicate. Archive reconciled 1:1, zero
decay. Still outstanding per docs/adding_a_benchmark_city.md: GT review,
the BENCHMARK_SPLITS allowlist, and the downstream pooled numbers.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SF2s8wWUM6HphV1xuYju21
digest 7485886ea08f671b over 94 panos, verify clean. Written now rather
than after review so it records the bytes the reviewer actually sees —
Mapillary re-serves thumb_original_url from a signed, expiring URL, so
"the same pano id" is not a guarantee of the same pixels later.

docs/adding_a_benchmark_city.md still does not mention this step; it
postdates the runbook. Every other split carries one.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SF2s8wWUM6HphV1xuYju21
94/94 panos reviewed, all 111 detections judged, 190 missed-ramp marks.

  Precision 0.898 (95% CI 0.827-0.942)   unbiased 0.863
  Recall    0.390 (95% CI 0.331-0.451)   unbiased 0.325

Precision is unremarkable — mid-pack. Recall is roughly half the
next-worst split (clovis 0.650), and three things say it is a real
failure mode rather than a small or ramp-poor sample:

- Laurens is ramp-RICH: 2.65 ramps/pano, 3rd of 10 splits, ahead of
  richmond, annapolis and morgantown. Misses/pano is 1.62 against a
  0.45-1.19 range everywhere else.
- The misses are NOT far-field. In all nine other splits missed ramps
  sit nearer the horizon than detected ones (delta -0.013 to -0.030 in
  normalized y); Laurens is the only split where the delta is positive
  (+0.004). It is missing near, well-resolved ramps.
- Not a lighting artifact either. The reviewer noted leaf litter on
  three panos, but measured over a 2%-width window the missed and
  detected ramps sit in the same light: median luminance 94.6 vs 97.4,
  47% vs 42% in shadow. The leaf/shadow reading is not supported.

What the miss crops do show, visually and so far unmeasured, is flush
or minimal-reveal ramps — a street cross-section close to at-grade,
which is the rural design this split was added to probe.

The GSV arm of this same town (runs/laurens_gsv, 2,137 panos, 2024-09,
leaf-free) is the discriminator and is not yet reviewed: if its recall
is also ~0.35 the cause is ramp geometry, if ~0.75 it is rig or season.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SF2s8wWUM6HphV1xuYju21
Phase 4-5 of docs/adding_a_benchmark_city.md.

Extraction + gate:
  operating_point_curve.py extract --cities laurens  (94 panos, local GPU)
  low_floor_sweep.py parity --cities laurens -> 111/111, 100.0% EXACT,
  med R 0.000. Bit-exact, which is what a Mapillary split should be.

Registries: US_SPLITS in low_floor_sweep and miss_decomposition, TIER
(mapillary), BENCHMARK_SPLITS, SERIES, and SPLIT_HEADER in
scoreboard_render (a touchpoint the Phase 5 table does not list).
tier_of() already resolves GoPro Max to action-modern, so no new branch.
SERIES takes slot 7 (#4a3aa7) -- the categorical slot the manual_gold
fold vacated, so no new hue is invented.

Pooled numbers move, as the runbook warns they will:

  thr 0.30   P 0.9002 -> 0.8991   R 0.7927 -> 0.7640   (-2.9 pts)
  thr 0.55   P 0.9637 -> 0.9594   R 0.7223 -> 0.6864   (-3.6 pts)
  n_panos 859 -> 953

Precision is untouched; the recall drop is Laurens' own 0.390 entering
the pool, not a regression anywhere else.

farfield_forensics.py and silent_activation.py both read the live
US_SPLITS but are FROZEN studies -- their tests pin the committed result
JSONs to numbers quoted in the issues' section 0c. Registering a city
silently restated both. Each now carries its own PUBLISHED_SPLITS /
US_SPLITS tuple with the reason, and their tests read that instead, so
the published findings stay reproducible and a future split cannot
rewrite them by accident. Laurens is absent from those two studies for
that reason alone; it is pooled everywhere else.

Also refreshed: op/*.csv (sweep, hist, gtbias, floor, distance),
op_cache/laurens.json, scoreboard.json, model_scoreboard.md and the five
scoreboard figures. The split date range test moves to 2026-08-31,
Laurens' review date.

NOT resolved here -- eight tests in test_scoreboard.py still fail, and
they should not be silenced by bumping constants. No challenger model
has been scored on Laurens, so every one of them becomes partial
coverage (7/8) and drops out of the pooled tables, and
test_ap_ordering_is_not_an_artifact_of_the_floor now compares RampNet's
8-split AP against a 7-split YOLO AP -- 0.829 vs 0.730, a margin of
0.0993 against an asserted 0.1. That is a population mismatch, not a
stale constant. See the PR for the decision it needs.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SF2s8wWUM6HphV1xuYju21
@jonfroehlich

Copy link
Copy Markdown
Member Author

Deep review

Re-ran the branch head (0c8e0f4) in a clean worktree. The split itself is sound and every number the PR body derives from a committed script reproduces exactly. score_validation.py benchmark/laurens prints P 0.898 (0.827–0.942) / R 0.390 (0.331–0.451), unbiased 0.863 / 0.325, to the digit. low_floor_sweep.py parity --cities laurens reports 111/111, 100.0% exact, med R 0.000. low_floor_sweep.py sweep regenerates analysis_out/op/low_floor_sweep.csv with zero value differences across all 756 rows, and its POOLED rows are the PR's table (0.9002 → 0.8991, 0.7927 → 0.7640 at 0.30; 0.9637 → 0.9594, 0.7223 → 0.6864 at 0.55; n 859 → 953). The imagery-manifest digest 7485886ea08f671b re-derives from the committed sha256 list; verdict, record and manifest pano ids are the same 94-element set; 111 detections judged = 111 in records.jsonl. The suite is 8 failed, 1314 passed, 1 skipped, and the eight are exactly the test_scoreboard.py tests the body names, no others. 2.65 ramps/pano (3rd of the ten city splits) and 1.62 misses/pano (next highest 1.18) re-derive by hand from analysis_out/scoreboard.json.

Two things changed underneath this PR since it was opened, and the review has to be read against them. PR #153 (fix/laurens-challenger-coverage, base benchmark/laurens) is stacked on this branch: it renames laurenslaurens_mapillary, adds the laurens_gsv arm, scores all 11 challengers on both, and updates the eight test constants after doing so. And #151 was amended on 2026-09-03: the near-field finding this PR's body leans on was withdrawn. Each finding below says whether #153 already resolves it.


1. High — verdicts.json carries no review_notes; the checklist says it does

benchmark/laurens/verdicts.json has the keys run_key, run_name, source, exported_at, panos and nothing else. The PR checklist ticks "review_notes written". The runbook (docs/adding_a_benchmark_city.md, Phase 2) makes it a required step — "reviewer confidence + what fought the rubric" — and every split added since the runbook existed carries one (paterson, gainesville, budapest, sao_paulo: reviewer, reviewed_at, confidence, summary, caveats). CLAUDE.md's replication rule is stricter still: human judgments are committed per rater, with the rubric they were made under.

So the split with the worst recall in the benchmark, by a factor of two, is the one whose reviewer, review date and confidence are recorded nowhere in the repo. Three per-pano notes exist (all three say leaves), and the gt_gallery.py panel that exports the block ("Your confidence in this pass") was evidently left empty.

Also worth recording in the block, because the PR body does not mention it: 38 of the 190 missed-ramp marks are unsure (20%; score_validation prints "+38 unsure, abstained"). That is the largest abstention count of any split and is itself a legibility signal for November GoPro imagery.

#153 does not fix this — it renames the file with zero content change, and laurens_gsv/verdicts.json has no review_notes either.

Fix: add a review_notes block in the same shape as gainesville's. The reviewer name, date (exported_at is 2026-08-31) and the leaf/abstention caveats can be written from the record; the confidence value has to come from whoever did the pass (see Decisions). A content edit under a downstream pure rename merges cleanly.

2. High — the PR body's central interpretation has been withdrawn, and the body still asserts it

The body says: "uniquely among all ten splits — its misses are near, not far-field (delta +0.004 in normalized y; every other split is −0.013 to −0.030)." Commit 517b3cf says the same, and adds the luminance/shadow numbers (94.6 vs 97.4; 47% vs 42%). Verified against #151: the issue body now carries an amendment block dated 2026-09-03 — "the second bullet under 'The finding' is withdrawn. The positive near-miss delta is arm-specific: laurens_gsv reads −0.0053, back to the normal far-field direction." #153's body says the same. #153's body gives the mapillary arm as +0.0038.

The withdrawn claim is not in any committed file on this branch — I grepped docs/, benchmark/README.md, scripts/analysis/, analysis_out/op/ for it and it appears only in the PR body and the commit message. So no doc needs rewriting here; the PR body does.

A second problem sits underneath it: none of the secondary measurements has a committed script. The normalized-y delta, the 2%-window luminance, the shadow fraction — nothing in scripts/ computes any of them (grepped ramps/pano, luminance, shadow, normali[sz]ed.y, delta_y), and #151's comments name no script either. Under CLAUDE.md a number produced by a script that does not exist in the repo is a memory, not a result. The fact that this one turned out to be an artifact is the argument for the rule.

Fix: edit the PR body — add an amendment block at the top of "The result" that withdraws the near-field bullet, points at #151's amendment and #153, and states that the y-delta and luminance figures came from an ad hoc measurement with no committed script. Do not rewrite the commit.

3. Medium — docs/model_scoreboard.md: the generated tables were regenerated, the prose around them was not

The file's hand-written text now contradicts the tables it wraps, on the same page:

line prose says the table beside it says
3 "Eighteen model legs, ten splits" eleven splits
7–12 "YOLO11l … 0.604; Gemini 3.1 Pro … 0.608 … a dead heat" neither model is in the headline table any more (both 7/8, partial)
13–14 "Its F1 spans 0.80–0.85 across the seven cities, a range of 0.053 … the only strong model that is also stable" F1 range column: 0.54–0.85
18–19 "0.969, above RampNet's 0.958 … against RampNet's 0.728" RampNet 0.951 / 0.686
24 "RampNet is the top score in all ten splits" eleven; and after #153 it is false on laurens_mapillary (y26_pano 0.574 > 0.543)
26–27 "clovis … is the worst pooled city for 5 of the 12 models" RampNet's worst is now laurens
34 "PR curves, pooled over the seven US splits" eight
42–44 "The pool is seven cities, not ten. … The three held-out splits" eight of eleven; three held-out becomes four after #153

The "stability" finding (line 13) is the one that matters scientifically: the sentence is now refuted by its own table, and a reader who trusts the prose gets the pre-Laurens picture.

#153 does not fix this — its model_scoreboard.md diff touches only lines inside the GENERATED markers. Prose edits outside those markers will merge cleanly; write them as "eight US splits" rather than naming laurens wherever possible so the downstream rename does not have to touch them.

4. Medium — the two operating-point figures were not regenerated

plot_operating_point.py gained the laurens SERIES entry and collect() reads ALL_SPLITS, so a regeneration would draw the eighth city — but docs/figures/operating_point_pr.png and docs/figures/storage_floor_ceiling.png are not in the diff (last touched bdd7d55, 2026-08-01). The committed figure is a seven-split plot sitting next to eight-split CSVs. The runbook's Phase 4 checklist has "Both figures regenerated and visually inspected" precisely because "an eighth series changes the legend layout and can collide with the curves". The body's "five figures regenerated" are the scoreboard figures, which are a different script.

#153 does not regenerate them either. Regenerate both here and look at them; #153 will need to run the same two scripts once more after its rename so the legend reads laurens_mapillary and includes the dashed laurens_gsv line it added to HELD_DASH.

5. Medium — the frozen-study fix was applied to two studies; three more have the same exposure

The PR's reasoning is right: a committed study whose script reads the live US_SPLITS is silently restated by every new registration. It was applied to farfield_forensics and silent_activation because their tests failed. But four more committed studies default --cities to the live tuple, and three of them do not even record which splits they covered:

committed result script default records its split list?
analysis_out/miss_decomposition.json US_SPLITS (now 8) no
analysis_out/miss_taxonomy.json US_SPLITS (now 8) no
analysis_out/silent_witness.json US_SPLITS (now 8) no
analysis_out/fp_taxonomy.json US_SPLITS (now 8) yes — cities: the seven

A default re-run of any of the first three now overwrites a seven-split result with an eight-split one, and nothing in the payload says which it is. None was re-run for this PR (they are unchanged in the diff), and the body does not say so — per CLAUDE.md, an unstated gap is indistinguishable from a hidden one. The numbers those files feed (docs/operating_point.md's "Pooled over the seven US splits" at lines 212, 265, 409; the #46 taxonomy rates) are therefore still seven-split figures under an eight-split registry.

Fix, minimal: have miss_decomposition.py, miss_taxonomy.py and silent_witness.py write "cities": [...] into their payloads (the same key fp_taxonomy and silent_activation already use), and add a line to the PR body naming these four studies as not re-run for Laurens. Whether to re-run them is a decision (below). #153 touches miss_decomposition.py in a different hunk (the registry lines), so this merges.

6. Medium-low — Phase 6 is not done, and one Phase 5 item is unstated

The body says benchmark/README.md, docs/model_comparison.md and docs/operating_point.md are not updated and gives the reason (the #55 pass). Two things go beyond that:

Stale split counts that this merge makes wrong and that #153 does not touch, for whoever does the docs pass: docs/model_comparison.md lines 3, 135, 143, 549, 766, 805, 919, 935, 1822; docs/operating_point.md lines 123, 188, 212, 373, 409, 762 (#153 rewrites the pooled block at 262–303 but not these); docs/replication.md "9 splits" at 227, 245, 282, 301, 448, 457 (#153 updates the file ledger, not the config counts).

7. Low

  • silent_activation.py pins its frozen tuple under the name US_SPLITS, the same name the live registry uses in every other module, while farfield_forensics.py chose PUBLISHED_SPLITS for the same thing. from silent_activation import US_SPLITS now returns a different tuple from from miss_decomposition import US_SPLITS, and the test has to alias it (as PUBLISHED_SPLITS) to say what it means. Rename to PUBLISHED_SPLITS in the script and the test.
  • The test_ap_ordering_is_not_an_artifact_of_the_floor margin, 0.0993 against an asserted 0.1, is a population mismatch as the body says — confirmed the other way round by The Laurens deficit is the rig, not the town: add the GSV arm and score all 11 challengers on both (#151) #153, which only moves the constants after scoring the challengers on the new split. Leaving the eight tests red rather than nudging the thresholds was the right call.
  • Strata 5/64/25 = 94: the geometric explanation checks out (sample.json records min_spacing_m: 30, selected: 94 from a 4,495-pano run), the unbiased column is 89 panos, and the body says the intervals are wider. Fine as stated.

Decisions for Jon, not fixes

What holds up

  • Every script-derived number in the body reproduces to the digit, and the parity gate is bit-exact as a Mapillary split should be.
  • The manifest was written before review, its digest re-derives, and the three id sets agree. The archive reconciliation claim (8.3 GB Mapillary, 40 GB GSV) is the one thing a clean clone cannot check; the body says where it lives.
  • The frozen-study pinning is correct and tested: test_the_recorded_scope_is_the_pooled_population asserts the committed payload's cities equals the frozen tuple, and test_farfield_forensics builds its population from PUBLISHED_SPLITS. Registering a city can no longer restate either study.
  • Registration is complete on every touchpoint the runbook lists, plus SPLIT_HEADER in scoreboard_render.py, which the runbook does not list and which The Laurens deficit is the rig, not the town: add the GSV arm and score all 11 challengers on both (#151) #153 keeps. tier_of() resolves GoPro Max to action-modern through the existing "max" in model branch, so no new tier was needed. SERIES slot 7 is the vacated categorical slot, not an invented hue.
  • Camera provenance is present on all 94 records; the copyright field matches the shape every other Mapillary split carries.
  • The eight CI failures are exactly the ones named, and refusing to bump the constants was correct.

Fix list

  • [F1] High Add a review_notes block to benchmark/laurens/verdicts.json in gainesville's shape (reviewer, reviewed_at = 2026-08-31, confidence, summary, caveats). Caveats must include: 38 of 190 missed marks abstained as unsure; leaf litter noted on three panos; strata 5/64/25 with an 89-pano unbiased column. If the reviewer's confidence is not known to you, write the block with confidence set to the string "unrecorded — reviewer to fill in" and say so in the PR comment; do not invent a level. Write with newline="", keep the existing key order, and confirm score_validation.py benchmark/laurens prints the block above the numbers — files: benchmark/laurens/verdicts.json
  • [F2] High Edit the PR body (gh pr edit 152): add an amendment block under "The result" withdrawing the "misses are near, not far-field (+0.004)" bullet, citing Recall collapses on flush / minimal-reveal curb ramps: Laurens R 0.390, and the experiment that isolates it #151's 2026-09-03 amendment and The Laurens deficit is the rig, not the town: add the GSV arm and score all 11 challengers on both (#151) #153 (laurens_gsv −0.0053); state that the y-delta, luminance and shadow figures came from an ad hoc measurement with no committed script; add a "Deliberate omissions" list naming the four studies in F5 as not re-run and the HF republish as waiting on The Laurens deficit is the rig, not the town: add the GSV arm and score all 11 challengers on both (#151) #153's rename; replace "The decision this needs" with a note that The Laurens deficit is the rig, not the town: add the GSV arm and score all 11 challengers on both (#151) #153 resolves it and gives the merge order. No session links, keep the existing attribution line — files: PR body only
  • [F3] Medium Rewrite the stale hand prose in docs/model_scoreboard.md outside the GENERATED markers so it agrees with the regenerated tables: line 3 split count; findings 1–3 (lines 7–19: the YOLO/Gemini dead-heat numbers are no longer in the headline table, RampNet's F1 range is 0.54–0.85 across eight cities and the "only stable model" sentence must go or be qualified by Laurens, RampNet's P/R are 0.951/0.686); line 24 "all ten splits"; lines 26–27 worst-city claim; line 34 caption; lines 42–44 pool description. Say "eight US splits" rather than naming laurens where the sentence allows, so The Laurens deficit is the rig, not the town: add the GSV arm and score all 11 challengers on both (#151) #153's rename does not have to touch it — files: docs/model_scoreboard.md
  • [F4] Medium Regenerate docs/figures/operating_point_pr.png (python scripts/analysis/plot_operating_point.py) and docs/figures/storage_floor_ceiling.png (python scripts/analysis/plot_storage_floor.py), open both and check the eighth series does not collide with the legend or the curves; note in the PR comment that The Laurens deficit is the rig, not the town: add the GSV arm and score all 11 challengers on both (#151) #153 must re-run both after its rename — files: docs/figures/operating_point_pr.png, docs/figures/storage_floor_ceiling.png
  • [F5] Medium Make scripts/analysis/miss_decomposition.py, scripts/analysis/miss_taxonomy.py and scripts/analysis/silent_witness.py write a "cities" list into their output payload (same key fp_taxonomy.py and silent_activation.py use), without regenerating the committed JSONs; add a module comment in each saying the committed result covers the seven pre-Laurens splits. Add one test that every committed study JSON under analysis_out/ produced by a script with a --cities default either carries cities or is named in an explicit allowlist with the reason — files: scripts/analysis/miss_decomposition.py, scripts/analysis/miss_taxonomy.py, scripts/analysis/silent_witness.py, tests/
  • [F6] Medium-low Update benchmark/README.md: add laurens rows to both split tables (all-panos and unbiased, from score_validation.py output) and a prose section in the shape of the other cities (what it is for, GoPro Max / Mapillary / 2025-11 / single uploader / 19 sequences, the leaf and abstention caveats, empty-group result), and fix the split counts at lines 81, 109, 117, 502. Name the split laurens as this branch does; The Laurens deficit is the rig, not the town: add the GSV arm and score all 11 challengers on both (#151) #153 will rename — files: benchmark/README.md
  • [F7] Low Rename silent_activation.py's frozen tuple from US_SPLITS to PUBLISHED_SPLITS and update tests/test_silent_activation.py to import it under that name, matching farfield_forensics.py — files: scripts/analysis/silent_activation.py, tests/test_silent_activation.py

🤖 Generated with Claude Code (claude-fable-5-1)

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