Skip to content

fix: prevent canonical-csv merge from collapsing distinct conferences - #404

Merged
JesperDramsch merged 3 commits into
mainfrom
claude/canonical-csv-merge-issue-he5hgi
Aug 2, 2026
Merged

fix: prevent canonical-csv merge from collapsing distinct conferences#404
JesperDramsch merged 3 commits into
mainfrom
claude/canonical-csv-merge-issue-he5hgi

Conversation

@JesperDramsch

@JesperDramsch JesperDramsch commented Aug 2, 2026

Copy link
Copy Markdown
Owner

Root cause analysis

A canonical-csv run (d76e737) renamed PyCon South Africa to PyCon Africa (grafting on africa.pycon.org sponsor/finaid/youtube links) and deleted the real PyCon Africa (Kampala, Uganda) entry.

Why it happened:

  1. conference_scorer takes the max of several fuzzy strategies. token_set_ratio returns 100 whenever one name's tokens are a subset of the other's — token_set_ratio("pycon africa", "pycon south africa") == 100 (verified; token_sort is 80, plain ratio 80, partial 74).
  2. fuzzy_match treated any score >= EXACT_MATCH_THRESHOLD (100) as an exact match and auto-merged with no confirmation prompt — so the CI-safe "default to no" path in query_yes_no never got a chance to reject it.
  3. Both YAML rows ("PyCon Africa" and "PyCon South Africa") then mapped to the same remote CSV row ("PyCon Africa"), got the same title_match index, and were silently collapsed into one chimera row by deduplicate() (bfill().ffill().iloc[0]).

The existing rejections.yml mechanism would have prevented this (it's checked before the exact-match branch), but the pair wasn't listed — this is the same class of bug as the already-listed PyCon Austria / PyCon Australia collision.

Fixes

  • utils/tidy_conf/interactive_merge.py
    • A score of 100 alone no longer qualifies as an exact match: the names must be genuinely identical (case/whitespace-insensitive) to auto-merge. Score-100-but-different pairs now go through the fuzzy confirmation path, which defaults to "no" in non-interactive CI.
    • A remote row that already belongs to an identically-named YAML entry (or was already claimed this run) can never be fuzzy-merged by a second conference — this blocks the two-rows-collapse-into-one failure mode entirely, independent of scoring.
  • utils/tidy_conf/data/rejections.yml — added PyCon AfricaPyCon South Africa / PyCon ZA / PyConZA as a known false-positive pair.
  • utils/check_data_loss.py + canonical-source-update.yml — new workflow step after the merge scripts that fails the run before anything is committed if any (conference, year) entry disappeared from conferences.yml/archive.yml/legacy.yml. Verified it flags the actual d76e737 diff (PyCon South Africa (2026) missing) and passes on identity.
  • tests/test_fuzzy_match.py — two regression tests reproducing the incident (with and without the identically-named YAML entry present); both conferences must survive with their own data.

Second bug: Google Maps link detected as Mastodon profile

The TBA-enrichment commit on the same PR (29e9231) wrote mastodon: https://www.google.com/maps/place/Transformatorhuis/@52.386807,... onto the FastAPI Conference entry. The generic Mastodon fallback in utils/enrich_tba.py accepted any URL with /@ in the path — and Google Maps viewports are /@lat,lng,zoom. The fallback now requires the path to be exactly /@handle (or /@user@instance), with regression tests in tests/test_youtube_extraction.py.

Third fix: sub-page links that just repeat the homepage

Upstream sources sometimes fill every URL column with the conference homepage — python-organizers' Proposal URL gave PyCon DE 2027 cfp_link: https://2027.pycon.de/, and main has more (PyCon Hong Kong cfp_link, PyCon Greece / PyCon Italy / PyCon Germany 2026 finaid). A cfp_link/sponsor/finaid identical to the main link carries no information, so the sort pipeline (drop_redundant_link_fields in utils/tidy_conf/links.py, wired into sort_yaml.py) now drops them. URLs count as identical only when they differ at most by scheme, www. prefix, or trailing slash — a different path, subdomain, query, or #anchor is a different pointer and survives (e.g. PyCon SG's sponsor: …/index.html#sponsors). Existing offenders on main get cleaned by the next weekly recurring-sort run; new ones are dropped before every automated commit. Tests in tests/test_redundant_links.py.

Testing

  • tests/test_fuzzy_match.py: 18 passed (16 existing + 2 new); test_interactive_merge.py: all pass; test_youtube_extraction.py: 10 passed (8 existing + 2 new); test_redundant_links.py: 13 new tests pass.
  • Full merge-related suite run against pip-installed deps: identical pass/fail set to unmodified main (the failures there are pre-existing environment/version artifacts, none touched by this change).
  • End-to-end check with the real data files (no mocks): remote "PyCon Africa" vs YAML "PyCon South Africa" now resolves via the rejection list; both conferences survive with correct link/place/dates.

The corresponding data repairs for the open conference-updates PR were pushed separately to auto/conference-updates (53e458a, ec8011a, fba6015). Note: until this PR is merged, a canonical-csv webhook run could still re-introduce the bad merge, since the workflow runs the merge scripts from the accumulator branch's code.

🤖 Generated with Claude Code

https://claude.ai/code/session_019bLVuyt4qdsvSpucA2qvgH

A canonical-csv run (d76e737) renamed PyCon South Africa to PyCon Africa
and deleted the real PyCon Africa entry. Root cause: conference_scorer
takes the max of several fuzzy strategies, and token_set_ratio returns
100 whenever one name's tokens are a subset of the other's - so
'PyCon South Africa' vs 'PyCon Africa' scored 100 and was auto-merged
as an 'exact' match with no confirmation. Both YAML rows then mapped to
the same remote row and were collapsed into one by deduplication.

Fixes:
- fuzzy_match only auto-merges when names are genuinely identical;
  score-100-but-different pairs now go through the confirmation path,
  which defaults to 'no' in CI
- remote rows already claimed by an identically-named YAML entry can
  never be fuzzy-merged by another conference
- add PyCon Africa <-> PyCon South Africa to rejections.yml
- add utils/check_data_loss.py and a workflow step that fails the
  canonical-source-update run before committing if any (conference,
  year) entry disappears from the data files
- regression tests reproducing the incident

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019bLVuyt4qdsvSpucA2qvgH
claude added 2 commits August 2, 2026 15:37
TBA enrichment's generic Mastodon fallback accepted any URL with '/@'
in the path, so a venue map link like
google.com/maps/place/Venue/@52.386807,4.8698442,17z was written into
the mastodon field (29e9231, FastAPI Conference). The generic fallback
now requires the path to be exactly /@handle (or /@user@instance).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019bLVuyt4qdsvSpucA2qvgH
Upstream sources sometimes fill every URL column with the conference
homepage (e.g. python-organizers' Proposal URL gave PyCon DE 2027
'cfp_link: https://2027.pycon.de/'). A cfp_link/sponsor/finaid identical
to the main link carries no information, so the sort pipeline now drops
them. URLs only count as identical when they differ at most by scheme,
'www.' prefix, or trailing slash - a different path, subdomain, query,
or #anchor is a different pointer and survives.

Existing offenders on main (PyCon Hong Kong cfp_link, PyCon Greece
finaid, PyCon Italy finaid, PyCon Germany finaid) will be cleaned by
the next recurring sort run.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019bLVuyt4qdsvSpucA2qvgH
@JesperDramsch
JesperDramsch marked this pull request as ready for review August 2, 2026 15:43
@JesperDramsch
JesperDramsch merged commit f48f1d7 into main Aug 2, 2026
11 checks passed
@JesperDramsch
JesperDramsch deleted the claude/canonical-csv-merge-issue-he5hgi branch August 2, 2026 15:47
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants