Description
Found investigating tri-comparison ledger shape m4/class/existence/agree[gitgalaxy]_vs[ctags] (4 occurrences).
gitgalaxy/standards/language_standards.py sets "class_start": None for 19 languages, a deliberate signal (per how_to_add_a_language.md's Rule 4) that the language genuinely has no class/OOP concept: shell, agc_assembly, makefile, xml, markdown, csv, pbtxt, yacc, m4, mlir, proto, hlo, td, plaintext, json, glsl, nix, blp, batch.
gitgalaxy/core/detector.py's class-extraction logic doesn't respect that signal, though:
class_start_pattern = (
self.languages.get(self.primary_lang_id, {}).get("rules", {}).get("class_start")
if self.primary_lang_id in _CLASS_START_NAMED_EXTRACTION_LANGS
else None
)
if class_start_pattern is not None:
class_matches = list(class_start_pattern.finditer(code_stream))
...
else:
# Legacy fallback: hardcoded `class|struct|interface|trait|enum` regex
class_start_pattern = re.compile(
r"^\s*(?:export\s+|public\s+|abstract\s+)?(?:class|struct|interface|trait|enum)\s+([a-zA-Z0-9_]+)..."
)
class_matches = list(class_start_pattern.finditer(code_stream))
There's no check distinguishing "this language's own class_start is None because it genuinely has no class concept" from "this language just isn't in the allowlist yet, use the generic fallback as a stopgap." Both cases fall into the same else branch, so every one of the 19 class_start: None languages gets the generic C-family-shaped fallback regex applied to its raw text regardless, with no protection against that pattern coincidentally matching unrelated text (macro arguments, embedded foreign-language snippets, sample code in markup, etc.).
Steps to Reproduce (confirmed, not theoretical)
language-crucible/data/m4/curl/configure.ac:1358:
struct SocketIFace *ISocket = NULL;
This is C source text embedded as a macro argument inside AC_LANG_PROGRAM([[ ... ]]) (an autoconf feature-test snippet) -- not real m4 structure, and not routed to a different language (detector.py's HANDSHAKE_REGISTRY mid-file language-switching only has triggers for <script>/<style>/inline-asm, nothing for autoconf's embedded-C convention). Because m4's class_start is None and m4 isn't in _CLASS_START_NAMED_EXTRACTION_LANGS either, the generic fallback regex runs over the whole file anyway and matches struct SocketIFace as if it were a real m4 class.
Confirmed directly against the live gatherer (not just the regex in isolation):
gather_language('m4')[...].gg_classes
# -> [('SocketIFace', None), ('Library', None), ('sockaddr_in6', None), ('timespec', None)]
4 false-positive "classes" across the m4 corpus, none of which are real m4 constructs -- ctags correctly reports none of them (ctags has no class-shaped kind for m4 at all).
Suggested fix
In detector.py's class-extraction branch, distinguish "this language's own class_start is explicitly None" (skip class extraction entirely, emit no classes) from "this language isn't in the allowlist but does have a real class_start regex not yet verified for named extraction" (current fallback behavior, unchanged) -- e.g. check self.languages.get(self.primary_lang_id, {}).get("rules", {}).get("class_start") is None directly, independent of allowlist membership, before falling through to the generic regex.
Scope note
Only m4 is confirmed as an active false-positive source right now (visible via the tri-comparison ledger, since it has ctags coverage to disagree with). The other 18 class_start: None languages listed above are NOT confirmed to be affected -- most don't have a comparison tool to catch this via the ledger, and the fallback regex's specific shape (class|struct|interface|trait|enum NAME) may simply never appear in their typical corpus content (e.g. json/csv/plaintext). Worth a quick corpus check per language once the fix lands, but that's a separate follow-up, not asserted here as confirmed.
Evidence
Full discrepancy record in docs/self_scan/tri_comparison_ledger.json, shape m4/class/existence/agree[gitgalaxy]_vs[ctags].
Description
Found investigating tri-comparison ledger shape
m4/class/existence/agree[gitgalaxy]_vs[ctags](4 occurrences).gitgalaxy/standards/language_standards.pysets"class_start": Nonefor 19 languages, a deliberate signal (perhow_to_add_a_language.md's Rule 4) that the language genuinely has no class/OOP concept:shell, agc_assembly, makefile, xml, markdown, csv, pbtxt, yacc, m4, mlir, proto, hlo, td, plaintext, json, glsl, nix, blp, batch.gitgalaxy/core/detector.py's class-extraction logic doesn't respect that signal, though:There's no check distinguishing "this language's own
class_startisNonebecause it genuinely has no class concept" from "this language just isn't in the allowlist yet, use the generic fallback as a stopgap." Both cases fall into the sameelsebranch, so every one of the 19class_start: Nonelanguages gets the generic C-family-shaped fallback regex applied to its raw text regardless, with no protection against that pattern coincidentally matching unrelated text (macro arguments, embedded foreign-language snippets, sample code in markup, etc.).Steps to Reproduce (confirmed, not theoretical)
language-crucible/data/m4/curl/configure.ac:1358:This is C source text embedded as a macro argument inside
AC_LANG_PROGRAM([[ ... ]])(an autoconf feature-test snippet) -- not real m4 structure, and not routed to a different language (detector.py'sHANDSHAKE_REGISTRYmid-file language-switching only has triggers for<script>/<style>/inline-asm, nothing for autoconf's embedded-C convention). Becausem4'sclass_startisNoneandm4isn't in_CLASS_START_NAMED_EXTRACTION_LANGSeither, the generic fallback regex runs over the whole file anyway and matchesstruct SocketIFaceas if it were a real m4 class.Confirmed directly against the live gatherer (not just the regex in isolation):
4 false-positive "classes" across the m4 corpus, none of which are real m4 constructs -- ctags correctly reports none of them (ctags has no class-shaped kind for m4 at all).
Suggested fix
In
detector.py's class-extraction branch, distinguish "this language's ownclass_startis explicitlyNone" (skip class extraction entirely, emit no classes) from "this language isn't in the allowlist but does have a realclass_startregex not yet verified for named extraction" (current fallback behavior, unchanged) -- e.g. checkself.languages.get(self.primary_lang_id, {}).get("rules", {}).get("class_start") is Nonedirectly, independent of allowlist membership, before falling through to the generic regex.Scope note
Only m4 is confirmed as an active false-positive source right now (visible via the tri-comparison ledger, since it has ctags coverage to disagree with). The other 18
class_start: Nonelanguages listed above are NOT confirmed to be affected -- most don't have a comparison tool to catch this via the ledger, and the fallback regex's specific shape (class|struct|interface|trait|enum NAME) may simply never appear in their typical corpus content (e.g. json/csv/plaintext). Worth a quick corpus check per language once the fix lands, but that's a separate follow-up, not asserted here as confirmed.Evidence
Full discrepancy record in
docs/self_scan/tri_comparison_ledger.json, shapem4/class/existence/agree[gitgalaxy]_vs[ctags].