climb13ers landed in peak_db — what changes here¶
For: mtn_research · From: peak_db / peak_checklist · Date: 2026-08-24
Answers: climb13ers-into-peak-db.md · tracking issue #22
The ask in that spec is done. peak_db now carries the climb13ers link as a first-class source. peak_db is the source of truth for it from here. Kyle: "mtn_research should use your db in the future."
What exists now¶
Three columns on peaks, alongside loj_url / fourteeners_url / peakbagger_url:
| column | example |
|---|---|
climb13ers_slug |
un13158 |
climb13ers_url |
https://www.climb13ers.com/colorado-13ers/un13158 |
climb13ers_link_how |
PRELIDAR |
597 CO 13ers linked, 597 distinct pages — no two peaks share a page. Provenance was kept per row as asked, so the soft tiers stay visibly weaker:
climb13ers_link_how |
rests on | n |
|---|---|---|
VERIFIED |
labelled summit marker on the page's own CalTopo map | 302 |
PRELIDAR |
pre-LiDAR slug and page elevation agrees | 221 |
REPAIRED |
elevation + range + name — relinked, see below | 48 |
EXACT |
page cross-references listsofjohn.com/peak/<id> |
17 |
MATCH |
elevation + name — a judgement, not a key | 7 |
REVIEWED |
Kyle confirmed by eye | 1 |
REPAIRED_UNNAMED |
elevation + range only — peak_db has no name to check | 1 |
MATCH and REPAIRED_UNNAMED are the two tiers resting on judgement rather than a key.
Read it with the shared client:
import sys
sys.path.insert(0, "/Users/kyleknutson/Library/Mobile Documents/com~apple~CloudDocs/shared/peak_db")
from peak_db_client import peaks
rows = peaks(state="eq.CO") # climb13ers_slug / _url / _link_how are on every row
⚠ The handover map mislinked 49 of the 597 rows¶
docs/data/climb13ers_map.json is not safe to re-import or regenerate. 49 rows in it
point at the wrong mountain — the same class of defect the spec was written to eliminate,
one axis over.
The spec's key insight was right: climb13ers keys its slugs to the pre-LiDAR elevation.
What it missed is that pre_lidar_elevation_ft is not unique. Five distinct peaks
share a pre-LiDAR 13,380. Each independently claimed the one page carrying that alias:
| peak_db | pointed at | what that page actually is |
|---|---|---|
| 108 Half Peak 13,866 | un13841--atlantic-peak |
Atlantic Peak, Tenmile |
| 503 James Pk 13,272 | un13294-the-citadel |
The Citadel |
| 737 Whale Pk 13,080 | un13078 |
a Sawatch peak ~13 mi off |
| 222 West Spanish Peak 13,584 | un13626 |
not it |
| 416 South Lookout Pk 13,420 | un-13380--triangle-peak |
Triangle Peak |
Root cause — scripts/sweep_climb13ers.py:627¶
cands = by_old_slug.get(old) # pages whose slug_ft or alias_ft == peak's pre-LiDAR
if len({c["slug"] for c in cands}) > 1: # ← only fires on PAGE-side ambiguity
cands = [c for c in cands if c["elevation"] == ft(p)]
if len({c["slug"] for c in cands}) == 1:
prelidar[p["id"]] = cands[0]["slug"]
The second key — page current elevation vs elevation_ft — is applied only when two
pages claim one pre-LiDAR number, never when two peaks do. So the tier documented as
"two keys, no inference" ran on one key for every peak-side collision. Five peaks with
current elevations 13378 / 13381 / 13420 / 13405 / 13365 all passed into one page; at most
one of them can be right.
Fix: apply the elevation filter unconditionally, not inside the > 1 branch. Peaks
that then fail it should fall through to the VERIFIED / EXACT / MATCH tiers, which
is what should have happened originally.
Second, smaller issue: by_old_slug indexes pages on both slug_ft and alias_ft.
For a page that was never renamed, slug_ft is its current elevation — so matching a
peak's pre-LiDAR number against it is exactly the cross-key confusion the spec's opening
un13155 example warns about. That produced id 171 (Grizzly Mountain), the one bad row
that was not a collision.
How the 49 were repaired¶
Every one had its own correctly-named page sitting unused — half-peak, james-peak,
whale-peak, west-spanish-peak. Relinked on exact current elevation + range, with name
agreement on 48 of 49 (id 381 is peak_db PT 13,494, which carries no name to check; the
page is peak-ten). Tagged REPAIRED so a correction never looks like a derivation —
issue #23's rule.
Then all 597 links were verified against the cached pages' own <title> elevations in
.cache/climb13ers/pages/: zero mismatches.
The full before/after is in docs/data/climb13ers_repaired.json — evidence for
checking a regenerated map against, not an input to import.
What to do here¶
- Read
climb13ers_urlfrom peak_db. Delete the local mapping table and all URL-derivation code. The defect the spec describes becomes impossible once nothing here builds a slug. - Rebind
check_climb13ers_url.pyto peak_db instead ofdocs/data/climb13ers_map.json(currentlyMAP_FILE, line 42; also referenced byrun_gates.py:130andtest_climb13ers_url.py:49). Keep the gate — it is the thing that should have caught the original wrong-peak citation. Worth a test that it fails when handed a URL for the wrong peak; that is the case it missed. - Do not re-run
sweep_climb13ers.pyuntil :627 is fixed. As it stands it will reintroduce all 49. If a regenerated map is ever re-imported into peak_db it silently overwrites theREPAIREDrows and puts Half Peak back on Atlantic Peak's page. - Retire
docs/data/climb13ers_map.jsononce (1) and (2) land, so there is one answer to "which page belongs to this peak" rather than two that can drift.
Nothing else was taken from climb13ers. It disagrees with 14ers/peak_db on elevations, names and disambiguator letters — 14ers remains the truth for facts; climb13ers contributes only the link. Letters are not a join key in either direction.
Also worth knowing¶
Both facts the spec asked to have written down are now in peak_checklist/schema.sql,
on the peaks.id column: a peak's id IS its Lists of John id, and out-of-state peaks
carry negative ids — 15 non-CO peaks (CA, HI, NM, NV, UT, WY) sit in the 13,000–14,000 ft
band, so filter a "Colorado" census on state, never on elevation alone.
The staleness check suggested in the spec is still the right approach and is unbuilt:
hash /colorado-13ers/peaks (one fetch, every slug + current elevation + "Formerly UN"
alias) to detect renames, rather than re-crawling 650 pages.
Unrelated: this repo has 2 unpushed commits¶
scripts/refresh_from_peakdb.py ran after the peak_db change (its normal hook) and
committed homestake_peak.md → climbed, but its push hit the pre-push gate:
- no recording of Kyle's in
gpx/homestake_peak/_kyle_existing/ - the drive link ends 2.22 mi from where the recommended route starts
Both surfaced only because the peak flipped to climbed, subjecting the report to gates it
had not faced before. Neither relates to climb13ers. A second commit, 031f37f ("Home page
catches up with Homestake Peak being climbed"), came from outside that run and was left
alone. The gate was not overridden — that is Kyle's call.