Bash

git add builder/src/build_index.cpp builder/test/compare_indexes.cpp && git commit -m "$(cat <<'EOF'
fix: building address string corruption + comparison tool semantic mode

The building address strings were stored with an embedded null byte separator
(housenumber + "\0" + street), but std::string("x") + "\0" produces
std::string("x") + std::string("") — the null terminator of the C literal
makes it an empty string, so the separator was never embedded. This caused
both housenumber and street fields to contain the full concatenated string,
corrupting ~16M building addresses in Germany.

Fix: use std::pair<std::string,std::string> instead of embedded-null hack,
matching how node address strings are already stored.

Also adds --semantic mode to compare_indexes for cross-version comparison
that matches by name/attributes with coarse coordinate grid.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
EOF
)"

Commit address string fix


[main d0b3540] fix: building address string corruption + comparison tool semantic mode
 2 files changed, 76 insertions(+), 16 deletions(-)


Now let me build the fully fixed version, run it, and compare. Let me also check if the backtracking build is done.


cat /tmp/claude-0/-root-traccar-geocoder/af1c4baa-0f9a-4420-a8b3-ce20dc28c8b9/tasks/bj0h5bxgn.output 2>/dev/null | tail -15

Check backtracking build output


  Merging thread-local results...
S2 cell computation complete.
Resolving interpolation endpoints...
Resolved 8/78 interpolation ways
Deduplicating...
Writing index files to /tmp/de-parallel-bt...
geo index: 9770600 cells (3312673 ways, 19951156 addrs, 78 interps)
admin index: 6103 cells, 43179 polygons
strings.bin: 149049857 bytes
Done.


Backtracking build done: **43,179 admin polygons** (up from 43,178 with the greedy stitcher — only 1 more). The backtracking didn't help much. Let me check the diff.


ssh node3 "/tmp/diff_admin /tmp/de-parallel-bt /tmp/de-sequential-fixed 2>&1 | head -30"

Compare backtracking parallel vs sequential


New code: 43179 polygons
Old code: 43221 polygons
Missing from new: 55
Extra in new: 13

=== Missing by admin level ===
  Level 2: 1 polygons
  Level 6: 2 polygons
  Level 8: 6 polygons
  Level 9: 7 polygons
  Level 10: 37 polygons
  Level 11: 2 polygons

=== Top 20 missing names ===
  2x Großfischbach
  1x Wäschegrund
  1x Werl
  1x Wense-Fallingbostel
  1x Wald-Michelbach
  1x Walchensee
  1x Verr
  1x Umbertshausen
  1x Süd
  1x Steinacker
  1x Solalinden
  1x Siedelsbrunn
  1x Schwaigwall
  1x Sauschütt
  1x Sankt Johannisrain
  1x Putzbrunn


The backtracking only gained 1 polygon — the issue isn't branch selection. The 55 missing polygons are likely caused by **missing way geometries**. Let me add diagnostics to find the root cause.


Edit

                                const auto& rel = data.collected_relations[i];
                                auto rings = assemble_outer_rings(rel.members, data.way_geometries);

                                for (auto& ring : rings) {

                                const auto& rel = data.collected_relations[i];
                                auto rings = assemble_outer_rings(rel.members, data.way_geometries);

                                // Diagnostic: log relations with 0 rings produced
                                if (rings.empty() && !rel.members.empty()) {
                                    int total_outer = 0, found = 0, missing = 0;
                                    for (const auto& [way_id, role] : rel.members) {
                                        if (role == "outer" || role.empty()) {
                                            total_outer++;
                                            auto it = data.way_geometries.find(way_id);
                                            if (it != data.way_geometries.end() && !it->second.coords.empty()) {
                                                found++;
                                            } else {
                                                missing++;
                                            }
                                        }
                                    }
                                    std::cerr << "  WARN: relation '" << rel.name
                                              << "' (level " << (int)rel.admin_level
                                              << ", " << rel.members.size() << " members): "
                                              << "0 rings from " << total_outer << " outer ways ("
                                              << found << " found, " << missing << " missing)" << std::endl;
                                }

                                for (auto& ring : rings) {