From 845a2a76f03ec58a17469a57102b054371e57e3a Mon Sep 17 00:00:00 2001 From: mkdev11 Date: Thu, 14 May 2026 14:51:56 +0200 Subject: [PATCH 1/3] fix: line-count score configured null-language extensions instead of skipping them MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Extensions in programming_languages.json with no language field (no tree-sitter parser) were silently scored as skipped-unsupported because the routing gate in calculate_token_score_from_file_changes only checked the NON_CODE_EXTENSIONS constant, not the JSON config. Affected extensions: env, gitattributes, gitignore, gql, graphql, ipynb, move, nim, pde, puml, tex. Fix: - Add TokenConfig.is_line_count_extension() that returns True when an extension is in NON_CODE_EXTENSIONS OR is configured in programming_languages.json with language=None. - Replace the ext in NON_CODE_EXTENSIONS gate with weights.is_line_count_extension(ext). - Remove the now-unused NON_CODE_EXTENSIONS import from tree_sitter_scoring. Tests: - Add TestNullLanguageLineCountScoring with regressions for graphql and gitignore (positive), unknown extension (negative control), graphql weight-accuracy pin, and a config-coverage guard that iterates every null-language entry in programming_languages.json and asserts it reaches line-count — catching future config/code drift before it zeroes miner scores. --- gittensor/validator/utils/load_weights.py | 16 ++++ .../validator/utils/tree_sitter_scoring.py | 3 +- .../test_token_scoring_integration.py | 88 +++++++++++++++++++ 3 files changed, 105 insertions(+), 2 deletions(-) diff --git a/gittensor/validator/utils/load_weights.py b/gittensor/validator/utils/load_weights.py index d1c8fee52..09dd66e59 100644 --- a/gittensor/validator/utils/load_weights.py +++ b/gittensor/validator/utils/load_weights.py @@ -95,6 +95,22 @@ def supports_tree_sitter(self, extension: Optional[str]) -> bool: config = self.language_configs.get(ext) return config is not None and config.language is not None + def is_line_count_extension(self, extension: Optional[str]) -> bool: + """Check if a file extension uses line-count scoring. + + Returns True when the extension is either in NON_CODE_EXTENSIONS or is + configured in programming_languages.json with no tree-sitter language + (language=None). Unknown extensions that are absent from the config + entirely remain skipped-unsupported and return False. + """ + if not extension: + return False + ext = extension.lstrip('.').lower() + if ext in NON_CODE_EXTENSIONS: + return True + config = self.language_configs.get(ext) + return config is not None and config.language is None + def _get_weights_dir() -> Path: return Path(__file__).parent.parent / 'weights' diff --git a/gittensor/validator/utils/tree_sitter_scoring.py b/gittensor/validator/utils/tree_sitter_scoring.py index b58e29aa9..cb408056c 100644 --- a/gittensor/validator/utils/tree_sitter_scoring.py +++ b/gittensor/validator/utils/tree_sitter_scoring.py @@ -19,7 +19,6 @@ INLINE_TEST_PATTERNS, MAX_FILE_SIZE_BYTES, MAX_LINES_SCORED_FOR_NON_CODE_EXT, - NON_CODE_EXTENSIONS, TEST_FILE_CONTRIBUTION_WEIGHT, TREE_SITTER_PARSE_TIMEOUT_MICROS, ) @@ -277,7 +276,7 @@ def calculate_token_score_from_file_changes( is_test_file=is_test_file, scoring_method='skipped', ) - elif ext in NON_CODE_EXTENSIONS: + elif weights.is_line_count_extension(ext): lines_to_score = min(file.changes, MAX_LINES_SCORED_FOR_NON_CODE_EXT) lang_config = programming_languages.get(ext) lang_weight = lang_config.weight if lang_config else DEFAULT_PROGRAMMING_LANGUAGE_WEIGHT diff --git a/tests/validator/test_token_scoring_integration.py b/tests/validator/test_token_scoring_integration.py index 0cebf26d2..b88a1a1c6 100644 --- a/tests/validator/test_token_scoring_integration.py +++ b/tests/validator/test_token_scoring_integration.py @@ -533,5 +533,93 @@ def test_pinned_python_control_flow(self, weights): assert breakdown.total_score == pytest.approx(9.99, abs=1e-6) +class TestNullLanguageLineCountScoring: + """Regression tests for configured null-language extensions. + + Every extension in programming_languages.json that has no ``language`` + field (no tree-sitter parser) must route to ``line-count`` scoring, not + ``skipped-unsupported``. Unknown extensions that are absent from the + config entirely should remain ``skipped-unsupported``. + """ + + @pytest.fixture + def weights(self) -> TokenConfig: + return load_token_config() + + @pytest.fixture + def prog_langs(self): + return load_programming_language_weights() + + def _score_file(self, filename: str, content: str, weights: TokenConfig, prog_langs): + fc = FileChange( + pr_number=1, + repository_full_name='test/repo', + filename=filename, + changes=content.count('\n') or 1, + additions=content.count('\n') or 1, + deletions=0, + status='added', + ) + result = calculate_token_score_from_file_changes( + [fc], + {filename: FileContentPair(old_content=None, new_content=content)}, + weights, + prog_langs, + ) + return result.file_results[0] + + def test_graphql_scores_line_count(self, weights, prog_langs): + fr = self._score_file('schema.graphql', 'type Query { hello: String }\n', weights, prog_langs) + assert fr.scoring_method == 'line-count' + assert fr.score > 0.0 + + def test_gitignore_scores_line_count(self, weights, prog_langs): + fr = self._score_file('.gitignore', 'node_modules/\n*.pyc\n', weights, prog_langs) + assert fr.scoring_method == 'line-count' + assert fr.score > 0.0 + + def test_unknown_extension_stays_skipped_unsupported(self, weights, prog_langs): + fr = self._score_file('data.xyz123', 'some content\n', weights, prog_langs) + assert fr.scoring_method == 'skipped-unsupported' + assert fr.score == 0.0 + + def test_graphql_score_uses_configured_weight(self, weights, prog_langs): + """graphql weight=1.0; one changed line → score should equal 1.0.""" + fc = FileChange( + pr_number=1, + repository_full_name='test/repo', + filename='schema.graphql', + changes=1, + additions=1, + deletions=0, + status='added', + ) + result = calculate_token_score_from_file_changes( + [fc], + {'schema.graphql': FileContentPair(old_content=None, new_content='type Query { hello: String }\n')}, + weights, + prog_langs, + ) + fr = result.file_results[0] + assert fr.scoring_method == 'line-count' + assert fr.score == pytest.approx(1.0, abs=1e-6) + + def test_all_configured_null_language_extensions_are_line_count_reachable(self, weights, prog_langs): + """Config-coverage guard: every programming_languages.json entry with language=None + must route to line-count, not skipped-unsupported. This catches future drift + where a new null-language weight is added to the JSON but the scorer silently + ignores it.""" + null_lang_exts = [ext for ext, cfg in prog_langs.items() if cfg.language is None] + assert null_lang_exts, 'Expected at least one null-language extension in programming_languages.json' + + for ext in null_lang_exts: + filename = f'testfile.{ext}' + fr = self._score_file(filename, 'line one\nline two\n', weights, prog_langs) + assert fr.scoring_method == 'line-count', ( + f'Extension .{ext} is configured in programming_languages.json with language=None ' + f'but scored as {fr.scoring_method!r} instead of line-count' + ) + + if __name__ == '__main__': pytest.main([__file__, '-v']) From 1ea48cd8eb0e3edf1ecedabde8a023eb703f79c4 Mon Sep 17 00:00:00 2001 From: mkdev11 Date: Fri, 22 May 2026 23:28:59 +0200 Subject: [PATCH 2/3] fix: remove dead null-language scoring config --- gittensor/constants.py | 1 + gittensor/validator/utils/load_weights.py | 17 ---- .../validator/utils/tree_sitter_scoring.py | 3 +- .../weights/programming_languages.json | 10 --- .../test_token_scoring_integration.py | 88 ------------------- 5 files changed, 3 insertions(+), 116 deletions(-) diff --git a/gittensor/constants.py b/gittensor/constants.py index 84fa8dbdf..a09867f27 100644 --- a/gittensor/constants.py +++ b/gittensor/constants.py @@ -49,6 +49,7 @@ 'markdown', 'txt', 'text', + 'tex', 'rst', 'adoc', 'asciidoc', diff --git a/gittensor/validator/utils/load_weights.py b/gittensor/validator/utils/load_weights.py index 516bfefaa..923db5017 100644 --- a/gittensor/validator/utils/load_weights.py +++ b/gittensor/validator/utils/load_weights.py @@ -99,23 +99,6 @@ def supports_tree_sitter(self, extension: Optional[str]) -> bool: config = self.language_configs.get(ext) return config is not None and config.language is not None - def is_line_count_extension(self, extension: Optional[str]) -> bool: - """Check if a file extension uses line-count scoring. - - Returns True when the extension is either in NON_CODE_EXTENSIONS or is - configured in programming_languages.json with no tree-sitter language - (language=None). Unknown extensions that are absent from the config - entirely remain skipped-unsupported and return False. - """ - if not extension: - return False - ext = extension.lstrip('.').lower() - if ext in NON_CODE_EXTENSIONS: - return True - config = self.language_configs.get(ext) - return config is not None and config.language is None - - def _get_weights_dir() -> Path: return Path(__file__).parent.parent / 'weights' diff --git a/gittensor/validator/utils/tree_sitter_scoring.py b/gittensor/validator/utils/tree_sitter_scoring.py index 1dc275adf..e22984fc4 100644 --- a/gittensor/validator/utils/tree_sitter_scoring.py +++ b/gittensor/validator/utils/tree_sitter_scoring.py @@ -19,6 +19,7 @@ INLINE_TEST_PATTERNS, MAX_FILE_SIZE_BYTES, MAX_LINES_SCORED_FOR_NON_CODE_EXT, + NON_CODE_EXTENSIONS, TEST_FILE_CONTRIBUTION_WEIGHT, TREE_SITTER_PARSE_TIMEOUT_MICROS, ) @@ -276,7 +277,7 @@ def calculate_token_score_from_file_changes( is_test_file=is_test_file, scoring_method='skipped', ) - elif weights.is_line_count_extension(ext): + elif ext in NON_CODE_EXTENSIONS: lines_to_score = min(file.changes, MAX_LINES_SCORED_FOR_NON_CODE_EXT) lang_config = programming_languages.get(ext) lang_weight = lang_config.weight if lang_config else DEFAULT_PROGRAMMING_LANGUAGE_WEIGHT diff --git a/gittensor/validator/weights/programming_languages.json b/gittensor/validator/weights/programming_languages.json index 0293b0aaf..b6ae7876d 100644 --- a/gittensor/validator/weights/programming_languages.json +++ b/gittensor/validator/weights/programming_languages.json @@ -27,7 +27,6 @@ "dart": { "weight": 1.0, "language": "dart" }, "dockerfile": { "weight": 1.0, "language": "dockerfile" }, "elm": { "weight": 1.75, "language": "elm" }, - "env": { "weight": 0.25 }, "erb": { "weight": 0.35 }, "erl": { "weight": 1.5, "language": "erlang" }, "ex": { "weight": 1.5, "language": "elixir" }, @@ -37,14 +36,10 @@ "f95": { "weight": 1.75, "language": "fortran" }, "fish": { "weight": 1.6, "language": "fish" }, "gd": { "weight": 1.5, "language": "gdscript" }, - "gitattributes": { "weight": 0.25 }, - "gitignore": { "weight": 0.5 }, "gleam": { "weight": 1.5, "language": "gleam" }, "glsl": { "weight": 1.5, "language": "glsl" }, "go": { "weight": 2.0, "language": "go" }, - "gql": { "weight": 1.0 }, "gradle": { "weight": 1.0, "language": "groovy" }, - "graphql": { "weight": 1.0 }, "groovy": { "weight": 1.0, "language": "groovy" }, "h": { "weight": 1.8, "language": "c" }, "hcl": { "weight": 1.0, "language": "hcl" }, @@ -58,7 +53,6 @@ "hxx": { "weight": 1.8, "language": "cpp" }, "ini": { "weight": 0.5 }, "ino": { "weight": 1.75, "language": "cpp" }, - "ipynb": { "weight": 0.3 }, "java": { "weight": 2.0, "language": "java" }, "jl": { "weight": 1.0, "language": "julia" }, "js": { "weight": 1.15, "language": "javascript" }, @@ -80,12 +74,9 @@ "ml": { "weight": 1.75, "language": "ocaml" }, "mli": { "weight": 1.75, "language": "ocaml" }, "mm": { "weight": 1.75, "language": "objc" }, - "move": { "weight": 1.0 }, "mts": { "weight": 1.20, "language": "typescript" }, - "nim": { "weight": 1.5 }, "nix": { "weight": 1.0, "language": "nix" }, "pas": { "weight": 1.5, "language": "pascal" }, - "pde": { "weight": 1.0 }, "php": { "weight": 1.25, "language": "php" }, "pl": { "weight": 1.0, "language": "perl" }, "plist": { "weight": 0.5 }, @@ -95,7 +86,6 @@ "properties": { "weight": 0.5 }, "proto": { "weight": 1.0, "language": "proto" }, "ps1": { "weight": 1.6, "language": "powershell" }, - "puml": { "weight": 0.1 }, "purs": { "weight": 1.75, "language": "purescript" }, "py": { "weight": 1.5, "language": "python" }, "pyi": { "weight": 1.5, "language": "python" }, diff --git a/tests/validator/test_token_scoring_integration.py b/tests/validator/test_token_scoring_integration.py index 1ec756a76..61bb7b515 100644 --- a/tests/validator/test_token_scoring_integration.py +++ b/tests/validator/test_token_scoring_integration.py @@ -617,93 +617,5 @@ def test_added_file_null_old_content_scores_as_new_file(self, weights): assert file_result.nodes_scored > 0 -class TestNullLanguageLineCountScoring: - """Regression tests for configured null-language extensions. - - Every extension in programming_languages.json that has no ``language`` - field (no tree-sitter parser) must route to ``line-count`` scoring, not - ``skipped-unsupported``. Unknown extensions that are absent from the - config entirely should remain ``skipped-unsupported``. - """ - - @pytest.fixture - def weights(self) -> TokenConfig: - return load_token_config() - - @pytest.fixture - def prog_langs(self): - return load_programming_language_weights() - - def _score_file(self, filename: str, content: str, weights: TokenConfig, prog_langs): - fc = FileChange( - pr_number=1, - repository_full_name='test/repo', - filename=filename, - changes=content.count('\n') or 1, - additions=content.count('\n') or 1, - deletions=0, - status='added', - ) - result = calculate_token_score_from_file_changes( - [fc], - {filename: FileContentPair(old_content=None, new_content=content)}, - weights, - prog_langs, - ) - return result.file_results[0] - - def test_graphql_scores_line_count(self, weights, prog_langs): - fr = self._score_file('schema.graphql', 'type Query { hello: String }\n', weights, prog_langs) - assert fr.scoring_method == 'line-count' - assert fr.score > 0.0 - - def test_gitignore_scores_line_count(self, weights, prog_langs): - fr = self._score_file('.gitignore', 'node_modules/\n*.pyc\n', weights, prog_langs) - assert fr.scoring_method == 'line-count' - assert fr.score > 0.0 - - def test_unknown_extension_stays_skipped_unsupported(self, weights, prog_langs): - fr = self._score_file('data.xyz123', 'some content\n', weights, prog_langs) - assert fr.scoring_method == 'skipped-unsupported' - assert fr.score == 0.0 - - def test_graphql_score_uses_configured_weight(self, weights, prog_langs): - """graphql weight=1.0; one changed line → score should equal 1.0.""" - fc = FileChange( - pr_number=1, - repository_full_name='test/repo', - filename='schema.graphql', - changes=1, - additions=1, - deletions=0, - status='added', - ) - result = calculate_token_score_from_file_changes( - [fc], - {'schema.graphql': FileContentPair(old_content=None, new_content='type Query { hello: String }\n')}, - weights, - prog_langs, - ) - fr = result.file_results[0] - assert fr.scoring_method == 'line-count' - assert fr.score == pytest.approx(1.0, abs=1e-6) - - def test_all_configured_null_language_extensions_are_line_count_reachable(self, weights, prog_langs): - """Config-coverage guard: every programming_languages.json entry with language=None - must route to line-count, not skipped-unsupported. This catches future drift - where a new null-language weight is added to the JSON but the scorer silently - ignores it.""" - null_lang_exts = [ext for ext, cfg in prog_langs.items() if cfg.language is None] - assert null_lang_exts, 'Expected at least one null-language extension in programming_languages.json' - - for ext in null_lang_exts: - filename = f'testfile.{ext}' - fr = self._score_file(filename, 'line one\nline two\n', weights, prog_langs) - assert fr.scoring_method == 'line-count', ( - f'Extension .{ext} is configured in programming_languages.json with language=None ' - f'but scored as {fr.scoring_method!r} instead of line-count' - ) - - if __name__ == '__main__': pytest.main([__file__, '-v']) From 13828416ed80c25af18e585b5235bb4ba8820ae2 Mon Sep 17 00:00:00 2001 From: mkdev11 Date: Fri, 22 May 2026 23:30:49 +0200 Subject: [PATCH 3/3] chore: restore load weights spacing --- gittensor/validator/utils/load_weights.py | 1 + 1 file changed, 1 insertion(+) diff --git a/gittensor/validator/utils/load_weights.py b/gittensor/validator/utils/load_weights.py index 923db5017..747755346 100644 --- a/gittensor/validator/utils/load_weights.py +++ b/gittensor/validator/utils/load_weights.py @@ -99,6 +99,7 @@ def supports_tree_sitter(self, extension: Optional[str]) -> bool: config = self.language_configs.get(ext) return config is not None and config.language is not None + def _get_weights_dir() -> Path: return Path(__file__).parent.parent / 'weights'