feat: prepare Radixor 3.0.0 with contracted tries and compiled patch commands

Introduce contracted compiled patch tries for faster lookup, make compiled
patch commands the primary runtime path, refresh stemmer benchmarks and
documentation, and restructure the documentation for 3.0.0 onboarding.

BREAKING CHANGE: Radixor 3.0.0 promotes compiled patch-command APIs and
new compiled trie artifacts as the primary runtime integration model.
This commit is contained in:
2026-07-03 18:44:39 +02:00
parent df4552b113
commit 38620d7e71
101 changed files with 11235 additions and 727 deletions

View File

@@ -6,7 +6,6 @@ This script derives compact machine-readable badge payloads from:
- JaCoCo XML coverage report
- PIT mutation testing XML report
- JMH CSV benchmark report
The generated JSON files are intended to be consumed by Shields endpoint badges.
"""
@@ -14,7 +13,6 @@ The generated JSON files are intended to be consumed by Shields endpoint badges.
from __future__ import annotations
import argparse
import csv
import json
import os
from pathlib import Path
@@ -38,8 +36,8 @@ def parse_args() -> argparse.Namespace:
)
parser.add_argument(
"--jmh-csv",
required=True,
help="Path to the JMH CSV report."
required=False,
help="Deprecated compatibility option. JMH speed badges are no longer generated."
)
parser.add_argument(
"--run-metrics-dir",
@@ -60,6 +58,12 @@ def write_json(target: Path, payload: dict[str, object]) -> None:
target.write_text(json.dumps(payload, indent=2) + os.linesep, encoding="utf-8")
def remove_file_if_present(target: Path) -> None:
"""Remove a previously generated file when it is present."""
if target.is_file():
target.unlink()
def unavailable_payload(label: str) -> dict[str, object]:
"""Create a standard payload for unavailable metrics."""
return {
@@ -83,19 +87,6 @@ def color_for_percentage(value: float) -> str:
return "red"
def color_for_speedup(value: float) -> str:
"""Select a badge color for a speedup factor."""
if value >= 4.0:
return "brightgreen"
if value >= 3.0:
return "green"
if value >= 2.0:
return "yellow"
if value >= 1.0:
return "orange"
return "red"
def coverage_payload(jacoco_xml: Path) -> dict[str, object]:
"""Build a line coverage badge payload from a JaCoCo XML report."""
if not jacoco_xml.is_file():
@@ -158,94 +149,27 @@ def mutation_payload(pit_xml: Path) -> dict[str, object]:
}
def parse_family_count(row: dict[str, str]) -> int:
"""Extract the JMH familyCount parameter from a CSV row."""
for key, value in row.items():
if key.startswith("Param: ") and key.endswith("familyCount"):
try:
return int(value)
except (TypeError, ValueError):
return -1
return -1
def benchmark_payload(jmh_csv: Path) -> dict[str, object]:
"""Build a benchmark speedup badge payload from a JMH CSV report."""
if not jmh_csv.is_file():
return unavailable_payload("english benchmark")
with jmh_csv.open("r", encoding="utf-8", newline="") as input_file:
rows = list(csv.DictReader(input_file))
if not rows:
return unavailable_payload("english benchmark")
relevant_rows: list[tuple[int, str, float]] = []
for row in rows:
benchmark = row.get("Benchmark", "")
if not benchmark.endswith(
"EnglishStemmerComparisonBenchmark.radixorUsUkProfiPreferredStem"
) and not benchmark.endswith(
"EnglishStemmerComparisonBenchmark.snowballOriginalPorter"
):
continue
try:
score = float(row["Score"])
except (KeyError, TypeError, ValueError):
continue
relevant_rows.append((parse_family_count(row), benchmark, score))
if not relevant_rows:
return unavailable_payload("english benchmark")
best_family_count = max(family_count for family_count, _, _ in relevant_rows)
radixor_score = None
porter_score = None
for family_count, benchmark, score in relevant_rows:
if family_count != best_family_count:
continue
if benchmark.endswith(".radixorUsUkProfiPreferredStem"):
radixor_score = score
elif benchmark.endswith(".snowballOriginalPorter"):
porter_score = score
if radixor_score is None or porter_score is None or porter_score <= 0.0:
return unavailable_payload("english benchmark")
# score is time for the batch processing, i.e. longer => slower, i.e. speedup is porter/radixor
speedup = porter_score / radixor_score
family_suffix = "" if best_family_count < 0 else f" ({best_family_count})"
return {
"schemaVersion": 1,
"label": "english benchmark",
"message": f"{speedup:.1f}x vs Porter{family_suffix}",
"color": color_for_speedup(speedup)
}
def main() -> int:
"""Generate all requested badge metadata files."""
arguments = parse_args()
jacoco_xml = Path(arguments.jacoco_xml)
pit_xml = Path(arguments.pit_xml)
jmh_csv = Path(arguments.jmh_csv)
run_metrics_dir = Path(arguments.run_metrics_dir)
latest_metrics_dir = Path(arguments.latest_metrics_dir)
payloads = {
"coverage-badge.json": coverage_payload(jacoco_xml),
"pitest-badge.json": mutation_payload(pit_xml),
"jmh-badge.json": benchmark_payload(jmh_csv)
"pitest-badge.json": mutation_payload(pit_xml)
}
for file_name, payload in payloads.items():
write_json(run_metrics_dir / file_name, payload)
write_json(latest_metrics_dir / file_name, payload)
remove_file_if_present(run_metrics_dir / "jmh-badge.json")
remove_file_if_present(latest_metrics_dir / "jmh-badge.json")
return 0