Skip to content

fix(tracers): make an id unique, by three mechanisms instead of one - #21

Merged
plearaj merged 1 commit into
claude/skill-improvements-mapping-violations-5yc9vhfrom
claude/pre-release-issues-lejoi3
Sep 2, 2026
Merged

fix(tracers): make an id unique, by three mechanisms instead of one#21
plearaj merged 1 commit into
claude/skill-improvements-mapping-violations-5yc9vhfrom
claude/pre-release-issues-lejoi3

Conversation

@plearaj

@plearaj plearaj commented Sep 2, 2026

Copy link
Copy Markdown
Owner

Resolves the one finding #20 recorded and deliberately left open: duplicated function ids on ordinary third-party code. Stacked on #20, so the diff here is the one commit.

An id is what a call graph refers to a function by, and /code-flow.quality computes unreached by subtracting reached ids from catalogued ones. Two functions sharing an id means reaching either marks both reached — so a genuinely unreachable function is silently absent from the findings, which is the exact failure mode detectorsSkipped and the "ran and found nothing" line exist to prevent.

The rule for an ordinary function is unchanged

derive_id is untouched. A function that never collides gets the same string it has always been given, so a hand-written map still joins against a traced one. All three suffixes below are applied only where the step before them left two records sharing an id.

What changed in assign_ids

Was Now
Counts names derived ids__add__ and add, a Java Builder and its builder() factory, ~Widget and Widget, _M_x and _M_X are two names and one id, and all went unsuffixed
Two on one line nothing could separate them a position suffix — ..._l7_1, ..._l7_2 — counting that line's same-id definitions in source order. 105 of PrimeVue's 107 were this
Two files, one id nothing could see it a file-rank suffix — src_service_describe_f1 for src/service.cpp, ..._f2 for src/service.hpp; likewise distutils/_msvccompiler.py beside distutils/msvccompiler.py

The file-rank suffix is the one part of the rule that looks outside a single file, because the collision is between two of them. It is applied as narrowly as the collision: only to the ids two files both derived, never to the rest of either file. Keying it off the id rather than off the file stem also closes the case where two files with different stems still meet on one id.

Measured

Over the corpora #20 measured, re-deriving the old ids from the same trace output — so the two columns are two rules over one catalog, not two runs that could differ for another reason:

Corpus Functions Duplicated ids
CPython 3.11 standard library 14,720 158 → 0
/usr/include 12,765 106 → 0
PrimeVue 5.0.1 5,817 107 → 0
TypeScript 5.6.3 compiler 20,707 4 → 0
Commons Lang 3.14 + Gson 2.10 4,594 3 → 0

Between 2.6% and 3.8% of ids change — the colliding ones and the groups they belong to, nothing else. scripts/build-map.py over PrimeVue, the run that first tripped the uniqueness assertion, now completes.

Two things worth a reviewer's eye

  • The C fixture is un-dodged. 1768daf moved Describable out of service.hpp to avoid the .cpp/.hpp collision rather than cover it. That is reverted, so the suite proves the fix on the shape that exposed it, and test_ids_are_unique_within_one_trace stops being a canary for a hazard the rule allows.
  • The TypeScript tracer assigned ids inside the collection walk, which cannot see a cross-file collision at all — and silently overwrote repo.byId when one happened. Ids and the by-id index now come after the walk, in one pass over every file.

Documentation

The id rule is stated verbatim in four host templates (shared/code-flow-map/SKILL.md, Claude, Gemini, Copilot) and in the tracers' README. All five now state all three suffixes, so an LLM writing a map by hand reaches the same ids a tracer does. RELEASE-CHECK-2026-08-28.md records the resolution against its own measurements; the changelog gets a Fixed entry.

Still outstanding for the release, and not addressable here: step 4 of scripts/prepublish-check.js — loading the skills on a real host — needs a Claude Code, Copilot, Codex or Antigravity surface that this environment does not have.

608 pytest, 76 node.

🤖 Generated with Claude Code

https://claude.ai/code/session_01GfQ1KbfqUeC1JFjC2F6g8v

The v1.2.0 manual pass found duplicated ids on ordinary third-party code --
158 in the CPython standard library, 107 in PrimeVue, 106 in `/usr/include`,
4 in the TypeScript compiler -- and recorded them unresolved. This resolves
them.

An id is what a call graph refers to a function by, and `/code-flow.quality`
computes `unreached` by subtracting reached ids from catalogued ones. Two
functions sharing an id means reaching either marks both reached, so a
genuinely unreachable function is silently absent from the findings: the exact
failure mode `detectorsSkipped` and the "ran and found nothing" line exist to
prevent.

`derive_id` is untouched, so an ordinary function's id is the same string it
has always been and a hand-written map still agrees with a traced one. What
changes is `assign_ids`, which counted names where the id rule folds names:

- It now counts **derived ids**. `__add__` and `add`, a Java `Builder` and its
  `builder()` factory, `~Widget` and `Widget`, `_M_x` and `_M_X` are all two
  names and one id, and all of them went unsuffixed and shared it.
- Two definitions on one line get `_<n>`, their position among that line's
  same-id definitions. `_l<line>` cannot separate them and no record carries a
  column. 105 of PrimeVue's 107 were this.
- Two files that derive one id get `_f<rank>`, each file's position among those
  paths sorted -- `service.cpp` beside `service.hpp`, since the rule drops the
  extension, or `distutils/_msvccompiler.py` beside `distutils/msvccompiler.py`,
  since it collapses underscore runs. The one part of the rule that looks
  outside a single file, so it is applied only to the ids two files both
  derived, never to the rest of either file.

Measured over the same corpora, re-deriving the old ids from the same trace
output so the two columns are two rules over one catalog: 158 -> 0, 107 -> 0,
106 -> 0, 4 -> 0. Between 2.6% and 3.8% of ids change, all of them colliding
ones. `scripts/build-map.py` over PrimeVue, the run that first tripped the
uniqueness assertion, now completes.

The C fixture was rearranged in 1768daf to dodge the `service.cpp` /
`service.hpp` collision rather than cover it; that is reverted, so the suite
proves the fix on the shape that exposed it.

The TypeScript tracer assigned ids per file inside the collection walk, which
cannot see a cross-file collision at all -- and silently overwrote `byId` when
one happened. Ids and the by-id index now come after the walk.

The id rule is stated verbatim in four host templates and in the tracers'
README; all five say all of this.

608 pytest, 76 node.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GfQ1KbfqUeC1JFjC2F6g8v
@plearaj
plearaj marked this pull request as ready for review September 2, 2026 22:59
@plearaj
plearaj merged commit d93adc0 into claude/skill-improvements-mapping-violations-5yc9vh Sep 2, 2026
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.

2 participants