Skip to content

Tri-comparison chart: drop recall/args ratios for unranked found-counts - #1868

Merged
squid-protocol merged 8 commits into
mainfrom
tri-comparison-clause-fix
Aug 19, 2026
Merged

Tri-comparison chart: drop recall/args ratios for unranked found-counts#1868
squid-protocol merged 8 commits into
mainfrom
tri-comparison-clause-fix

Conversation

@squid-protocol

Copy link
Copy Markdown
Owner

Summary

Follow-up to #1865 (already merged). PR #1865's branch was reused for this work since it built directly on that fix; #1865 itself is unaffected -- this PR is just the 2 commits on top of it that hadn't merged yet.

  • Func/Class Recall panels removed as ratios, replaced with Functions Found/Classes Found (raw per-tool counts, no denominator, no winner badge, excluded from the summary tally). The old matched / union-of-everyone's-claims denominator sounded principled but turned out exactly as corruptible as any single tool's own noise -- confirmed twice on real data this session: ctags' Haskell parser double/triple-tags multi-clause functions, and tree-sitter's own raw walk had the identical clause-splitting bug (fixed in Fix haskell clause-splitting in tri-comparison's tree-sitter walk #1865) before that. Either bug inflates the shared denominator, silently deflating every other tool's recall percentage too.
  • Args Match dropped the same way, for the same reason -- its matched_consensus already meant "matched the cross-tool majority vote," the same unverified-agreement-as-correctness assumption, just expressed as a 2-vs-1 majority credit instead of a union denominator. Now Args Found: each tool's raw comparable-args count, no ratio, no badge.
  • Func/Class Precision panels are unchanged -- their denominator is each tool's own claim count, never a cross-tool union or majority vote, so they can't be corrupted the same way. This is already the "of what we found, how many are real" framing.
  • No changes to tri_comparison_reconcile.py/tri_comparison_ledger.py -- purely a chart-rendering change; the underlying per-slot agreement data was already computed this way.

This is explicitly framed as temporary in the code comments: once a language/metric's ledger shapes are validated (per docs/self_scan/how_to_investigate_a_discrepancy.md), a real denominator can come back for that language specifically.

Verification

  • Test-rendered --languages haskell,rust before the full run and visually confirmed via PNG export: found-count panels show plain numbers with no badges (e.g. haskell Functions Found: GitGalaxy=148, tree-sitter=146, ctags=180 -- the ctags multi-clause inflation is now a visible fact instead of implying a winner); precision/asterisk behavior unchanged.
  • Full tri_comparison_chart.py --all --write across all 45 languages.
  • Confirmed all 5 previously-validated haskell ledger entries survived the regeneration untouched.
  • ruff_audit.py --ci / mypy_audit.py --ci: no new findings beyond baseline.

Test plan

  • Full --all --write regeneration, diffed against prior state.
  • Visual PNG render check of the found-count panels (no ratio, no badge) and unchanged precision panels.
  • ruff_audit.py --ci / mypy_audit.py --ci clean.

🤖 Generated with Claude Code

squid-protocol and others added 8 commits August 19, 2026 10:51
_walk_tree_sitter listed every matching tree-sitter node with no
consolidation, so a Haskell function written as multiple pattern-match
clauses (idiomatic style -- toJSON instance methods, guard-clause
helpers, etc.) counted as multiple distinct occurrences of one name.
Confirmed via the ledger's own toJSON example: 5 clause-nodes at
consecutive lines in one Options.hs instance block, where GitGalaxy and
ctags each correctly report 1.

This is the same bug tree_sitter_accuracy_audit.py's measure() already
fixed once (#1614-1616) -- this module wrote its own simpler walk
instead of reusing that closure and silently reintroduced it. Ported
the same fix: consecutive func-type sibling nodes sharing a name
(interleaved `comment` nodes don't break the run) collapse into the
first occurrence only.

Verified: haskell's raw tree-sitter function count drops from 275 to
146 (GitGalaxy's own count is 148), and the ledger's
tree_sitter-only-vs-[ctags,gitgalaxy] shape (91 occurrences) no longer
reproduces. Regenerated the full ledger and chart across all 45
languages with tree-sitter-language-pack + a locally-built
universal-ctags -- no other language's entries were affected.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Resolved without needing a fresh investigation -- two from source already
read earlier in this same effort, one from a doc comment already sitting
in ctags_reader.py:

- agree[gitgalaxy]_vs[ctags,tree_sitter] (2): mixed verdict, read
  individually. getExtensions is a real instance method both other tools
  structurally can't see; extensionEnabled is a genuine GitGalaxy false
  positive (misreads a guard-clause call as a definition).
- args agree[none]_vs[gitgalaxy,tree_sitter] (9): one systematic cause,
  point-free/eta-reduced equations -- GitGalaxy counts true signature
  arity, tree-sitter counts only the aligned clause's explicit patterns.
  Same shape as Claim 1, not an engine defect.
- class agree[gitgalaxy,tree_sitter]_vs[ctags] (16): not a real
  discrepancy at all -- ctags_reader.py already documents
  CTAGS_CLASS_KINDS["haskell"] = set() on purpose, no class-shaped ctags
  kind exists for this language.

The remaining 2 unvalidated haskell shapes (ctags-alone existence,
ctags-misses-real existence) are still open -- under investigation.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
agree[ctags]_vs[gitgalaxy,tree_sitter] (103 occurrences): dispatched
agent investigation, all 10 sampled cases are ctags-side artifacts, no
GitGalaxy/tree-sitter defect. Three causes: multi-clause double-tagging
(ctags tags every pattern-match equation as a separate occurrence, GG/TS
correctly anchor to the first clause only), keyword-as-identifier
misparsing (class/where/pattern), and a value-vs-function kind collapse
ctags' Haskell parser can't express (GitGalaxy and tree-sitter's own
audit tooling both independently make the same correct distinction, per
#1312/#1566). Full evidence in the ledger verdict.

5 of 8 haskell shapes now validated. 1 remaining under investigation.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
agree[gitgalaxy,tree_sitter]_vs[ctags] (69 occurrences): dispatched agent
investigation confirms all 10 sampled misses -- and by cross-check
against further non-sampled instances, plausibly all 69 -- are
locally-scoped definitions (instance methods, where-clause helpers,
let-bound names in do blocks), never top-level. ctags' Haskell parser
has no layout-rule/scope awareness and only tags column-1 equations; it
correctly handles multi-clause definitions when they're top-level
(expandFilterPath/writeFnBinary/writerFn all tag fine), so this is a
pure scope gap distinct from the clause-splitting bug fixed earlier.
GitGalaxy and tree-sitter both correct; nothing to fix in this repo.

Added a doc addendum to ctags_reader.py alongside its existing Haskell
notes so this is documented at the source, not just in the ledger.

All 8 haskell ledger shapes are now resolved: 5 validated (real
findings, all confirming GitGalaxy/tree-sitter correctness against
ctags-side limitations, plus one genuine GitGalaxy false-positive and
one documented point-free-arity distinction), 3 stale/no-longer-
reproducing (artifacts of the clause-splitting bug fixed earlier in
this PR, kept as history per the ledger's own lifecycle). Regenerated
chart.svg reflects the cleared asterisks.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
recall_t = matched / union-of-everyone's-claims looked principled but
shares the exact weakness any single tool's own noise: this session
found ctags multi-clause double-tagging (haskell) and, before that, a
tree-sitter clause-splitting bug that both inflate the SHARED union
denominator, silently deflating every OTHER tool's recall percentage in
the process. A ranking built on a denominator any one tool can corrupt
isn't a ranking worth showing.

Func Recall / Class Recall panels are now Functions Found / Classes
Found: each tool's raw claim count, no ratio, no winner badge, excluded
from the summary tally -- "found more" isn't a correctness claim (a
hallucinating tool finds more too). Precision panels are unchanged and
still meaningful: their denominator is each tool's OWN claim count,
never a cross-tool union, so it can't be corrupted the same way -- this
is already the "of what we found, how many are real" framing.

No changes to tri_comparison_reconcile.py/ledger.py -- the underlying
per-slot agreement data was already computed this way; only how the
chart renders two of the five panels changed. Regenerated across all 45
languages; confirmed Haskell's ctags multi-clause inflation (148 GG /
146 tree-sitter / 180 ctags) now renders as a plain, unranked fact
instead of implying a winner.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Args' matched_consensus already meant "matched the cross-tool majority
vote" -- exactly the same unverified-agreement-as-correctness assumption
just removed from Func/Class Recall, just expressed as a 2-vs-1 majority
credit instead of a union denominator. Either shape lets one tool's own
noise (ctags' Haskell multi-clause double-tagging, tree-sitter's
now-fixed clause-splitting bug) distort another tool's score.

Args Match -> Args Found: each tool's raw comparable-args count
(MetricScore.total_slots, not matched_consensus -- matched_consensus
would still be the majority-vote correctness claim we're trying not to
show pre-verification), no ratio, no badge, doesn't enter the summary
tally. Same treatment as Functions/Classes Found, using a different
underlying field since args' "found" concept isn't gate-free the way
func/class existence is (args' total_slots still requires >=2 tools to
have a comparable value -- an honest, noted limitation, not a silent
one).

Once a language/metric's ledger shapes are validated, a real
denominator can come back for that language -- this is deliberately
temporary, not a permanent downgrade. Summary tally is now Func/Class
Precision only (2 ranked panels, down from 3).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Real problem found on the regenerated chart: a strictly-highest-rate_pct
badge rule doesn't know or care about sample size or whether anyone has
actually checked the disagreement against source -- a 2-sample cell at
100% (2/2) could badge over an 80-sample cell at 98.75% (79/80) with
equal visual confidence. That's not a tool being more correct, it's an
artifact of an unverified comparison.

Replaces is_language_metric_clean() (GitGalaxy-only, aspect-specific)
with has_open_question() (tri_comparison_ledger.py): symmetric across
all three tools, one question -- does ANY currently-reproducing,
unvalidated ledger entry exist for this (language, symbol_type, metric)
triple, regardless of which tool(s) are on which side. Drives two
chart behaviors, both now symmetric instead of GitGalaxy-only:
  - EVERY tool's value label in a disputed cell gets `*`, not just
    GitGalaxy's -- the open question is "has anyone verified this",
    not "did GitGalaxy specifically lose".
  - No winner badge is drawn on a disputed cell at all, on any panel,
    regardless of how lopsided the raw numbers look.

Confirmed working as intended on the regenerated chart: haskell (5 of
8 ledger shapes validated this session) now shows zero asterisks and a
real badge on Func Precision; c and rust, never investigated, are
honestly asterisked across every panel with no badges at all. Earning
a badge now requires the same verification work already used for
haskell -- reading real source and recording a verdict in the ledger
-- not just a bigger number.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…-fix

# Conflicts:
#	docs/self_scan/tri_comparison_chart.svg
#	docs/self_scan/tri_comparison_ledger.json
@github-actions

Copy link
Copy Markdown
Contributor

🐦‍⬛ Muninn Security Scan

✅ No security issues found.

🐦‍⬛ Powered by Muninn · Skald Lab

@squid-protocol
squid-protocol merged commit dbc7118 into main Aug 19, 2026
28 checks passed
@squid-protocol
squid-protocol deleted the tri-comparison-clause-fix branch August 19, 2026 19:25
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