Repository navigation
PATH_MODIFIERS: a missing group makes tsx?$ a top-level alternative — every .ts/.tsx file in every scan gets structural_mass x0.1 #2769
Description
Second pass over the keyword-rosetta bias report, this time ranked by consistency score rather than by language (2026-09-05, engine 3b3568e0, corpus main 6183c4b). structural_mass reads 1.04 for typescript against 10.48 for javascript on the same 12-probe program. The cause is a missing group in analysis_lens.PATH_MODIFIERS, and it applies to every TypeScript file in every scan.
The rule
# PATH_MODIFIERS["Structural Mass"]
(re.compile(r"(?:^|/)(?:icons?|illustrations?|logos?|assets?)/.*\.jsx?|tsx?$"), 0.1),The intent is legible from the token list: files under an icons/, illustrations/, logos/ or assets/ directory carry little structural weight. The extension alternation was meant to read \.(?:jsx?|tsx?)$. As written, | binds at the top level, so the pattern is
(?:^|/)(?:icons?|illustrations?|logos?|assets?)/.*\.jsx? OR tsx?$
and the second alternative — a bare tsx?$, unanchored to any directory — matches any path ending in ts or tsx.
Repro
Three byte-identical files, no icons/ directory anywhere, galaxyscope --db-only:
file_name structural_mass total_loc
probe.js 6.16 9
probe.ts 0.62 9 <-- x0.1
widget.tsx 0.62 9 <-- x0.1
Direct check against the compiled rule:
>>> rgx.search("src/app/service.ts") # matches, x0.1
>>> rgx.search("components/Button.tsx") # matches, x0.1
>>> rgx.search("src/app/service.js") # no matchWhat reads it
galaxyscope.py:2301 applies the multiplier to forensic_result["file_impact"], which is the file's physical mass everywhere downstream:
record_keeper.py:708persists it asfile_data.structural_mass;spatial_mapper.py:228uses it as node mass in the repository graph;gpu_recorder.py:210scales the WebGPU mass;llm_recorder.py:803sorts by it to pick the top-25 files for the LLM brief;security_auditor.py:373reads it back asfile_impact;state_rehydrator.py:104restores it on incremental scans.
So in any TypeScript repository — one of the most commonly scanned language families — every file is 10× too light. "Top Structural Pillars", the LLM brief's file selection, and the graph's mass distribution all systematically rank TypeScript below equivalent JavaScript, and a mixed .js/.ts repo mid-migration ranks its migrated files last.
Candidate shape
(re.compile(r"(?:^|/)(?:icons?|illustrations?|logos?|assets?)/.*\.(?:jsx?|tsx?)$"), 0.1),Two adjacent entries in the same table have the identical shape and should be checked in the same pass — Structural Mass (?:^|/)(?:__snapshots__|__mocks__|fixtures?)/|.*\.snap$ and Documentation Exposure (?:^|/)(?:swagger|openapi)\.(?:json|yaml|yml)$|\.ipynb$ — both look deliberate (a .snap/.ipynb file anywhere is genuinely the target), but they are indistinguishable from this bug by inspection, so a comment saying "the top-level alternation is intended here" is worth adding while the file is open.
A regression guard belongs with the fix: a test that walks every PATH_MODIFIERS entry, and for each one asserts a path that should NOT match. probe.ts against the icons rule is exactly that assertion.
Blast radius
Every golden-master directory group containing .ts/.tsx files, structural_mass / file_impact columns only, values rising 10×. Large but mechanically attributable — one multiplier, one extension family.
Corpus pairing
keyword-rosetta/data/typescript re-blesses structural_mass 1.04 → ~10.4, joining the other mainstream languages inside the band. The path-and-extension-modifiers ledger entry's survey (2026-09-02, engine 3ac91822) enumerated five multiplier families and did not catch this one, because it surveyed which rules fire on the corpus's own paths without asking whether each one fired for the reason its tokens claim — worth noting in the entry as the method gap, since the same survey underwrites six other risk columns.
Activity