Reorganize task categories from opaque a/b to descriptive names

Replace category_a/category_b directories with algorithm, pipeline,
environment, filesystem, and process. Add separate mode field (solve/convert)
to decouple orchestration from capability grouping. Add per-category
summary and questionnaire breakdowns to both terminal report and HTML export.
This commit is contained in:
Cormac Shannon committed 2026-03-29 20:59:01 +01:00
1 parent be8d657b24
commit 20e62f60f6
18 files changed
+487 -167

No files matched your search

+1 -1
View File
@@ -112,7 +112,7 @@ def solve_task(
"""Run the agent loop: prompt -> code -> test -> retry."""
system = build_system_prompt(language)
if task.category == "b" and language == "lush":
if task.mode == "convert" and language == "lush":
user_prompt = build_conversion_prompt(task)
else:
user_prompt = build_task_prompt(task, language)
+166 -9
View File
@@ -88,11 +88,11 @@ def chart_questionnaire_comparison(results: list[BenchmarkResult]) -> str:
def chart_turns_comparison(results: list[BenchmarkResult]) -> str:
"""Bar chart of agent turns per task for bash vs lush."""
# Only include tasks where the agent actually solved (turns > 0)
cat_a = [r for r in results if r.category == "a"]
names = [r.task_name for r in cat_a]
bash_turns = [r.bash_result.agent_turns if r.bash_result else 0 for r in cat_a]
lush_turns = [r.lush_result.agent_turns if r.lush_result else 0 for r in cat_a]
# Only include tasks where the agent actually solved (solve mode)
solve = [r for r in results if r.mode == "solve"]
names = [r.task_name for r in solve]
bash_turns = [r.bash_result.agent_turns if r.bash_result else 0 for r in solve]
lush_turns = [r.lush_result.agent_turns if r.lush_result else 0 for r in solve]
fig, ax = plt.subplots(figsize=(8, 4))
x = range(len(names))
@@ -103,7 +103,7 @@ def chart_turns_comparison(results: list[BenchmarkResult]) -> str:
ax.set_xticks(list(x))
ax.set_xticklabels(names, rotation=35, ha="right", fontsize=8)
ax.set_ylabel("Agent Turns")
ax.set_title("Agent Turns to Solve (Category A)")
ax.set_title("Agent Turns to Solve (Solve Mode)")
ax.yaxis.set_major_locator(ticker.MaxNLocator(integer=True))
ax.legend()
ax.grid(axis="y", alpha=0.3)
@@ -148,6 +148,151 @@ def chart_per_task_heatmap(results: list[BenchmarkResult]) -> str:
return _fig_to_base64(fig)
def chart_per_category_questionnaire(results: list[BenchmarkResult]) -> str:
"""Grouped bar chart: one cluster per category, bars for bash/lush avg scores."""
from collections import defaultdict
by_cat: dict[str, list[BenchmarkResult]] = defaultdict(list)
for r in results:
by_cat[r.category].append(r)
categories = sorted(by_cat)
bash_avgs = []
lush_avgs = []
for cat in categories:
b_scores: list[float] = []
l_scores: list[float] = []
for r in by_cat[cat]:
scores = _get_likert_scores(r)
for key in scores:
if scores[key]["bash"] is not None:
b_scores.append(scores[key]["bash"])
if scores[key]["lush"] is not None:
l_scores.append(scores[key]["lush"])
bash_avgs.append(sum(b_scores) / len(b_scores) if b_scores else 0.0)
lush_avgs.append(sum(l_scores) / len(l_scores) if l_scores else 0.0)
fig, ax = plt.subplots(figsize=(8, 4))
x = range(len(categories))
bar_w = 0.35
bars_b = ax.bar([i - bar_w / 2 for i in x], bash_avgs, bar_w, label="bash", color=BASH_COLOR)
bars_l = ax.bar([i + bar_w / 2 for i in x], lush_avgs, bar_w, label="lush", color=LUSH_COLOR)
ax.set_xticks(list(x))
ax.set_xticklabels(categories, fontsize=9)
ax.set_ylim(0, 5.5)
ax.set_ylabel("Avg Score (1-5)")
ax.set_title("Questionnaire Scores by Category")
ax.legend()
ax.grid(axis="y", alpha=0.3)
for bar in bars_b:
ax.text(bar.get_x() + bar.get_width() / 2, bar.get_height() + 0.08, f"{bar.get_height():.1f}",
ha="center", va="bottom", fontsize=8)
for bar in bars_l:
ax.text(bar.get_x() + bar.get_width() / 2, bar.get_height() + 0.08, f"{bar.get_height():.1f}",
ha="center", va="bottom", fontsize=8)
return _fig_to_base64(fig)
def chart_per_category_radar(results: list[BenchmarkResult]) -> list[tuple[str, str]]:
"""Small-multiples bar charts: one per category showing 6 Likert dimensions for bash vs lush."""
import numpy as np
from collections import defaultdict
by_cat: dict[str, list[BenchmarkResult]] = defaultdict(list)
for r in results:
by_cat[r.category].append(r)
charts: list[tuple[str, str]] = []
labels = [label for _, label in LIKERT_QUESTIONS]
for cat in sorted(by_cat):
cat_results = by_cat[cat]
agg: dict[str, dict[str, list[float]]] = {}
for key, _ in LIKERT_QUESTIONS:
agg[key] = {"bash": [], "lush": []}
for r in cat_results:
scores = _get_likert_scores(r)
for key in scores:
for lang in ("bash", "lush"):
val = scores[key][lang]
if val is not None:
agg[key][lang].append(val)
bash_vals = [sum(agg[k]["bash"]) / len(agg[k]["bash"]) if agg[k]["bash"] else 0.0 for k, _ in LIKERT_QUESTIONS]
lush_vals = [sum(agg[k]["lush"]) / len(agg[k]["lush"]) if agg[k]["lush"] else 0.0 for k, _ in LIKERT_QUESTIONS]
fig, ax = plt.subplots(figsize=(6, 3.5))
y = range(len(labels))
bar_h = 0.35
ax.barh([i + bar_h / 2 for i in y], bash_vals, bar_h, label="bash", color=BASH_COLOR)
ax.barh([i - bar_h / 2 for i in y], lush_vals, bar_h, label="lush", color=LUSH_COLOR)
ax.set_yticks(list(y))
ax.set_yticklabels(labels, fontsize=8)
ax.set_xlim(0, 5.5)
ax.set_title(f"{cat}", fontsize=10)
ax.legend(fontsize=8, loc="lower right")
ax.invert_yaxis()
ax.grid(axis="x", alpha=0.3)
charts.append((cat, _fig_to_base64(fig)))
return charts
def _build_per_category_summary_html(results: list[BenchmarkResult]) -> str:
"""HTML table: rows=categories, columns=bash/lush pass rate, turns, scores."""
from collections import defaultdict
by_cat: dict[str, list[BenchmarkResult]] = defaultdict(list)
for r in results:
by_cat[r.category].append(r)
rows = []
for cat in sorted(by_cat):
cat_results = by_cat[cat]
b_passed = sum(1 for r in cat_results if r.bash_result and r.bash_result.all_passed)
l_passed = sum(1 for r in cat_results if r.lush_result and r.lush_result.all_passed)
b_total = sum(1 for r in cat_results if r.bash_result)
l_total = sum(1 for r in cat_results if r.lush_result)
b_turn_vals = [r.bash_result.agent_turns for r in cat_results if r.bash_result and r.bash_result.agent_turns > 0]
l_turn_vals = [r.lush_result.agent_turns for r in cat_results if r.lush_result and r.lush_result.agent_turns > 0]
b_turns_avg = sum(b_turn_vals) / len(b_turn_vals) if b_turn_vals else 0.0
l_turns_avg = sum(l_turn_vals) / len(l_turn_vals) if l_turn_vals else 0.0
b_scores: list[float] = []
l_scores: list[float] = []
for r in cat_results:
scores = _get_likert_scores(r)
for key in scores:
if scores[key]["bash"] is not None:
b_scores.append(scores[key]["bash"])
if scores[key]["lush"] is not None:
l_scores.append(scores[key]["lush"])
b_avg = sum(b_scores) / len(b_scores) if b_scores else 0.0
l_avg = sum(l_scores) / len(l_scores) if l_scores else 0.0
rows.append(f"""<tr>
<td>{html.escape(cat)}</td>
<td>{b_passed}/{b_total}</td><td>{l_passed}/{l_total}</td>
<td>{b_turns_avg:.1f}</td><td>{l_turns_avg:.1f}</td>
<td>{b_avg:.1f}</td><td>{l_avg:.1f}</td>
</tr>""")
return f"""<table>
<thead><tr>
<th>Category</th>
<th>Bash Pass</th><th>Lush Pass</th>
<th>Bash Avg Turns</th><th>Lush Avg Turns</th>
<th>Bash Avg Score</th><th>Lush Avg Score</th>
</tr></thead>
<tbody>{"".join(rows)}</tbody>
</table>"""
def _build_summary_html(results: list[BenchmarkResult]) -> str:
rows = []
for r in results:
@@ -160,7 +305,7 @@ def _build_summary_html(results: list[BenchmarkResult]) -> str:
b_turns = str(b.agent_turns) if b else "-"
l_turns = str(l.agent_turns) if l else "-"
rows.append(f"""<tr>
<td>{html.escape(r.task_name)}</td><td>{r.category.upper()}</td>
<td>{html.escape(r.task_name)}</td><td>{html.escape(r.category)}</td>
<td class="{b_cls}">{b_pass}</td><td>{b_turns}</td>
<td class="{l_cls}">{l_pass}</td><td>{l_turns}</td>
</tr>""")
@@ -215,7 +360,7 @@ def _build_detail_html(results: list[BenchmarkResult]) -> str:
sections.append(f"""
<div class="task-detail">
<h3>{html.escape(r.task_name)} <span class="cat">[{r.category}]</span>
<h3>{html.escape(r.task_name)} <span class="cat">[{r.category}/{r.mode}]</span>
<span class="{"pass" if b_status == "PASS" else "fail"}">bash={b_status}</span>
<span class="{"pass" if l_status == "PASS" else "fail"}">lush={l_status}</span>
</h3>
@@ -238,7 +383,10 @@ def export_html(results_dir: Path, output_path: Path) -> None:
chart_questionnaire = chart_questionnaire_comparison(results)
chart_turns = chart_turns_comparison(results)
chart_heatmap = chart_per_task_heatmap(results)
chart_cat_quest = chart_per_category_questionnaire(results)
cat_radar_charts = chart_per_category_radar(results)
summary_table = _build_summary_html(results)
cat_summary_table = _build_per_category_summary_html(results)
detail_html = _build_detail_html(results)
model = results[0].model if results else "unknown"
@@ -288,15 +436,24 @@ def export_html(results_dir: Path, output_path: Path) -> None:
<h2>Summary</h2>
{summary_table}
<h2>Per-Category Summary</h2>
{cat_summary_table}
<h2>Questionnaire Scores</h2>
<div class="chart"><img src="data:image/png;base64,{chart_questionnaire}" alt="Questionnaire comparison"></div>
<h2>Agent Turns (Category A)</h2>
<h2>Questionnaire Scores by Category</h2>
<div class="chart"><img src="data:image/png;base64,{chart_cat_quest}" alt="Per-category questionnaire"></div>
<h2>Agent Turns (Solve Mode)</h2>
<div class="chart"><img src="data:image/png;base64,{chart_turns}" alt="Turns comparison"></div>
<h2>Score Difference Heatmap (Lush - Bash)</h2>
<div class="chart"><img src="data:image/png;base64,{chart_heatmap}" alt="Score heatmap"></div>
<h2>Per-Category Breakdown</h2>
{"".join(f'<h3>{cat}</h3><div class="chart"><img src="data:image/png;base64,{img}" alt="{cat} breakdown"></div>' for cat, img in cat_radar_charts)}
<h2>Per-Task Detail</h2>
{detail_html}
+13 -7
View File
@@ -36,15 +36,17 @@ class TestCase:
@dataclass
class Task:
name: str
category: str # "a" or "b"
category: str # "algorithm", "pipeline", "environment", "filesystem", "process"
description: str
test_cases: list[TestCase]
bash_source: str | None = None # category B only
mode: str = "solve" # "solve" or "convert"
bash_source: str | None = None # convert mode only
def to_dict(self) -> dict[str, Any]:
d: dict[str, Any] = {
"name": self.name,
"category": self.category,
"mode": self.mode,
"description": self.description,
"test_cases": [tc.to_dict() for tc in self.test_cases],
}
@@ -59,6 +61,7 @@ class Task:
category=d["category"],
description=d["description"],
test_cases=[TestCase.from_dict(tc) for tc in d["test_cases"]],
mode=d.get("mode", "solve"),
bash_source=d.get("bash_source"),
)
@@ -180,16 +183,18 @@ class QuestionnaireResponse:
class BenchmarkResult:
task_name: str
category: str
provider: str
model: str
timestamp: str
bash_result: LanguageResult | None
lush_result: LanguageResult | None
mode: str = "solve" # "solve" or "convert"
provider: str = ""
model: str = ""
timestamp: str = ""
bash_result: LanguageResult | None = None
lush_result: LanguageResult | None = None
def to_dict(self) -> dict[str, Any]:
return {
"task_name": self.task_name,
"category": self.category,
"mode": self.mode,
"provider": self.provider,
"model": self.model,
"timestamp": self.timestamp,
@@ -202,6 +207,7 @@ class BenchmarkResult:
return cls(
task_name=d["task_name"],
category=d["category"],
mode=d.get("mode", "solve"),
provider=d["provider"],
model=d["model"],
timestamp=d["timestamp"],
+105 -8
View File
@@ -86,11 +86,11 @@ def render_summary_table(results: list[BenchmarkResult]) -> str:
lines.append("=" * 78)
lines.append("")
header = f" {'Task':<22s} {'Cat':>3s} {'Bash':^14s} {'Lush':^14s}"
header = f" {'Task':<22s} {'Category':<12s} {'Mode':<8s} {'Bash':^14s} {'Lush':^14s}"
lines.append(header)
sub = f" {'':<22s} {'':>3s} {'pass turns':^14s} {'pass turns':^14s}"
sub = f" {'':<22s} {'':<12s} {'':<8s} {'pass turns':^14s} {'pass turns':^14s}"
lines.append(sub)
lines.append(" " + "-" * 60)
lines.append(" " + "-" * 74)
for r in results:
b = r.bash_result
@@ -99,7 +99,7 @@ def render_summary_table(results: list[BenchmarkResult]) -> str:
l_pass = "PASS" if l and l.all_passed else "FAIL" if l else "-"
b_turns = str(b.agent_turns) if b else "-"
l_turns = str(l.agent_turns) if l else "-"
lines.append(f" {r.task_name:<22s} [{r.category}] {b_pass:>4s} {b_turns:>5s} {l_pass:>4s} {l_turns:>5s}")
lines.append(f" {r.task_name:<22s} {r.category:<12s} {r.mode:<8s} {b_pass:>4s} {b_turns:>5s} {l_pass:>4s} {l_turns:>5s}")
# Totals
b_passed = sum(1 for r in results if r.bash_result and r.bash_result.all_passed)
@@ -115,9 +115,9 @@ def render_summary_table(results: list[BenchmarkResult]) -> str:
if l_turn_counts:
l_turns_avg = sum(l_turn_counts) / len(l_turn_counts)
lines.append(" " + "-" * 60)
lines.append(f" {'TOTAL':<22s} {b_passed}/{b_total:>2d} {b_turns_avg:>5.1f} {l_passed}/{l_total:>2d} {l_turns_avg:>5.1f}")
lines.append(f" {'':27s}{'pass avg turns':^14s} {'pass avg turns':^14s}")
lines.append(" " + "-" * 74)
lines.append(f" {'TOTAL':<22s} {'':<12s} {'':<8s} {b_passed}/{b_total:>2d} {b_turns_avg:>5.1f} {l_passed}/{l_total:>2d} {l_turns_avg:>5.1f}")
lines.append(f" {'':<22s} {'':<12s} {'':<8s} {'pass avg turns':^14s} {'pass avg turns':^14s}")
lines.append("")
return "\n".join(lines)
@@ -172,6 +172,101 @@ def render_questionnaire_comparison(results: list[BenchmarkResult]) -> str:
return "\n".join(lines)
def render_per_category_summary(results: list[BenchmarkResult]) -> str:
"""Render per-category breakdown: pass rates, avg turns, avg questionnaire scores."""
from collections import defaultdict
lines: list[str] = []
lines.append("=" * 78)
lines.append(" PER-CATEGORY SUMMARY")
lines.append("=" * 78)
lines.append("")
# Group by category
by_cat: dict[str, list[BenchmarkResult]] = defaultdict(list)
for r in results:
by_cat[r.category].append(r)
header = f" {'Category':<12s} {'Bash pass':>9s} {'Lush pass':>9s} {'B turns':>7s} {'L turns':>7s} {'B score':>7s} {'L score':>7s}"
lines.append(header)
lines.append(" " + "-" * 70)
for cat in sorted(by_cat):
cat_results = by_cat[cat]
b_passed = sum(1 for r in cat_results if r.bash_result and r.bash_result.all_passed)
l_passed = sum(1 for r in cat_results if r.lush_result and r.lush_result.all_passed)
b_total = sum(1 for r in cat_results if r.bash_result)
l_total = sum(1 for r in cat_results if r.lush_result)
b_turn_vals = [r.bash_result.agent_turns for r in cat_results if r.bash_result and r.bash_result.agent_turns > 0]
l_turn_vals = [r.lush_result.agent_turns for r in cat_results if r.lush_result and r.lush_result.agent_turns > 0]
b_turns_avg = sum(b_turn_vals) / len(b_turn_vals) if b_turn_vals else 0.0
l_turns_avg = sum(l_turn_vals) / len(l_turn_vals) if l_turn_vals else 0.0
# Avg questionnaire scores
b_scores: list[float] = []
l_scores: list[float] = []
for r in cat_results:
scores = _get_likert_scores(r)
for key in scores:
if scores[key]["bash"] is not None:
b_scores.append(scores[key]["bash"])
if scores[key]["lush"] is not None:
l_scores.append(scores[key]["lush"])
b_avg_score = sum(b_scores) / len(b_scores) if b_scores else 0.0
l_avg_score = sum(l_scores) / len(l_scores) if l_scores else 0.0
lines.append(
f" {cat:<12s} {b_passed}/{b_total:>2d} {l_passed}/{l_total:>2d} "
f"{b_turns_avg:>5.1f} {l_turns_avg:>5.1f} {b_avg_score:>5.1f} {l_avg_score:>5.1f}"
)
lines.append("")
return "\n".join(lines)
def render_per_category_questionnaire(results: list[BenchmarkResult]) -> str:
"""Render per-category Likert averages."""
from collections import defaultdict
lines: list[str] = []
lines.append("=" * 78)
lines.append(" PER-CATEGORY QUESTIONNAIRE AVERAGES")
lines.append("=" * 78)
lines.append("")
by_cat: dict[str, list[BenchmarkResult]] = defaultdict(list)
for r in results:
by_cat[r.category].append(r)
for cat in sorted(by_cat):
cat_results = by_cat[cat]
lines.append(f" {cat}")
agg: dict[str, dict[str, list[float]]] = {}
for key, _ in LIKERT_QUESTIONS:
agg[key] = {"bash": [], "lush": []}
for r in cat_results:
scores = _get_likert_scores(r)
for key in scores:
for lang in ("bash", "lush"):
val = scores[key][lang]
if val is not None:
agg[key][lang].append(val)
for key, label in LIKERT_QUESTIONS:
b_vals = agg[key]["bash"]
l_vals = agg[key]["lush"]
b_avg = sum(b_vals) / len(b_vals) if b_vals else 0.0
l_avg = sum(l_vals) / len(l_vals) if l_vals else 0.0
diff = l_avg - b_avg
diff_str = f"+{diff:.1f}" if diff > 0 else f"{diff:.1f}" if diff < 0 else " 0.0"
lines.append(f" {label:<22s} bash={b_avg:.1f} lush={l_avg:.1f} ({diff_str})")
lines.append("")
return "\n".join(lines)
def render_per_task_detail(results: list[BenchmarkResult]) -> str:
"""Render per-task questionnaire breakdown."""
lines: list[str] = []
@@ -183,7 +278,7 @@ def render_per_task_detail(results: list[BenchmarkResult]) -> str:
lines.append("")
b_status = "PASS" if r.bash_result and r.bash_result.all_passed else "FAIL"
l_status = "PASS" if r.lush_result and r.lush_result.all_passed else "FAIL"
lines.append(f" {r.task_name} [{r.category}] bash={b_status} lush={l_status}")
lines.append(f" {r.task_name} [{r.category}/{r.mode}] bash={b_status} lush={l_status}")
lines.append("")
scores = _get_likert_scores(r)
@@ -222,7 +317,9 @@ def render_report(results_dir: Path) -> str:
parts = [
render_summary_table(results),
render_per_category_summary(results),
render_questionnaire_comparison(results),
render_per_category_questionnaire(results),
render_per_task_detail(results),
]
return "\n".join(parts)
+20 -14
View File
@@ -39,32 +39,35 @@ def load_task(path: Path) -> Task:
category=raw["category"],
description=raw["description"],
test_cases=test_cases,
mode=raw.get("mode", "solve"),
bash_source=raw.get("bash_source"),
)
def find_tasks(category: str | None = None) -> list[Path]:
def find_tasks(category: str | None = None, mode: str | None = None) -> list[Path]:
tasks_dir = Path(__file__).parent / "tasks"
paths = []
if category:
cat_dir = tasks_dir / f"category_{category}"
cat_dir = tasks_dir / category
if cat_dir.exists():
paths = sorted(cat_dir.glob("*.toml"))
else:
for cat_dir in sorted(tasks_dir.iterdir()):
if cat_dir.is_dir():
paths.extend(sorted(cat_dir.glob("*.toml")))
if mode:
paths = [p for p in paths if load_task(p).mode == mode]
return paths
def cmd_list_tasks(args: argparse.Namespace) -> None:
paths = find_tasks(args.category)
paths = find_tasks(args.category, getattr(args, "mode", None))
if not paths:
print("No tasks found.")
return
for p in paths:
task = load_task(p)
print(f" [{task.category}] {task.name:20s} {p.relative_to(Path.cwd())}")
print(f" [{task.category:<12s} {task.mode:<7s}] {task.name:20s} {p.relative_to(Path.cwd())}")
def cmd_run(args: argparse.Namespace) -> None:
@@ -81,13 +84,13 @@ def cmd_run(args: argparse.Namespace) -> None:
provider = PROVIDERS[provider_name](provider_config)
timestamp = datetime.now(timezone.utc).strftime("%Y%m%dT%H%M%SZ")
print(f"Running task: {task.name} (category {task.category}) with {provider.model_name}")
print(f"Running task: {task.name} ({task.category}/{task.mode}) with {provider.model_name}")
bash_result = None
lush_result = None
if task.category == "a":
# Category A: solve in both languages
if task.mode == "solve":
# Solve mode: agent writes code in both languages
print(" Solving in bash...")
bash_result = solve_task(provider, task, "bash", config)
print(f" Bash: {'PASS' if bash_result.all_passed else 'FAIL'} ({bash_result.agent_turns} turns)")
@@ -96,9 +99,9 @@ def cmd_run(args: argparse.Namespace) -> None:
lush_result = solve_task(provider, task, "lush", config)
print(f" Lush: {'PASS' if lush_result.all_passed else 'FAIL'} ({lush_result.agent_turns} turns)")
elif task.category == "b":
# Category B: verify provided bash source directly, then convert to lush
assert task.bash_source, f"Category B task {task.name} missing bash_source"
elif task.mode == "convert":
# Convert mode: verify provided bash source directly, then convert to lush
assert task.bash_source, f"Convert-mode task {task.name} missing bash_source"
print(" Verifying provided bash source...")
test_results = evaluate(task, task.bash_source, "bash", config)
all_passed = all(tr.passed for tr in test_results)
@@ -124,6 +127,7 @@ def cmd_run(args: argparse.Namespace) -> None:
benchmark = BenchmarkResult(
task_name=task.name,
category=task.category,
mode=task.mode,
provider=provider_name,
model=provider.model_name,
timestamp=timestamp,
@@ -136,7 +140,7 @@ def cmd_run(args: argparse.Namespace) -> None:
def cmd_run_all(args: argparse.Namespace) -> None:
paths = find_tasks(args.category)
paths = find_tasks(args.category, getattr(args, "mode", None))
if not paths:
print("No tasks found.")
return
@@ -164,7 +168,8 @@ def main() -> None:
# list-tasks
ls = sub.add_parser("list-tasks", help="List available tasks")
ls.add_argument("--category", choices=["a", "b"], help="Filter by category")
ls.add_argument("--category", choices=["algorithm", "pipeline", "environment", "filesystem", "process"], help="Filter by category")
ls.add_argument("--mode", choices=["solve", "convert"], help="Filter by mode")
ls.set_defaults(func=cmd_list_tasks)
# run
@@ -174,8 +179,9 @@ def main() -> None:
run.set_defaults(func=cmd_run)
# run-all
ra = sub.add_parser("run-all", help="Run all tasks in a category")
ra.add_argument("--category", choices=["a", "b"], help="Category to run")
ra = sub.add_parser("run-all", help="Run all tasks (optionally filtered)")
ra.add_argument("--category", choices=["algorithm", "pipeline", "environment", "filesystem", "process"], help="Filter by category")
ra.add_argument("--mode", choices=["solve", "convert"], help="Filter by mode")
ra.add_argument("--provider", default="anthropic", help="LLM provider")
ra.set_defaults(func=cmd_run_all)
+158 -116
View File
@@ -47,53 +47,53 @@
<th>Lush</th><th>Turns</th>
</tr></thead>
<tbody><tr>
<td>env_config</td><td>A</td>
<td>fizzbuzz</td><td>algorithm</td>
<td class="pass">PASS</td><td>1</td>
<td class="pass">PASS</td><td>1</td>
</tr><tr>
<td>reverse_string</td><td>algorithm</td>
<td class="pass">PASS</td><td>1</td>
<td class="pass">PASS</td><td>1</td>
</tr><tr>
<td>two_sum</td><td>algorithm</td>
<td class="pass">PASS</td><td>1</td>
<td class="pass">PASS</td><td>1</td>
</tr><tr>
<td>env_config</td><td>environment</td>
<td class="pass">PASS</td><td>3</td>
<td class="pass">PASS</td><td>2</td>
</tr><tr>
<td>file_organizer</td><td>A</td>
<td>env_path_builder</td><td>environment</td>
<td class="pass">PASS</td><td>0</td>
<td class="pass">PASS</td><td>1</td>
</tr><tr>
<td>file_organizer</td><td>filesystem</td>
<td class="fail">FAIL</td><td>4</td>
<td class="pass">PASS</td><td>1</td>
</tr><tr>
<td>fizzbuzz</td><td>A</td>
<td class="pass">PASS</td><td>1</td>
<td class="pass">PASS</td><td>1</td>
</tr><tr>
<td>multi_file_search</td><td>A</td>
<td>multi_file_search</td><td>filesystem</td>
<td class="pass">PASS</td><td>1</td>
<td class="pass">PASS</td><td>3</td>
</tr><tr>
<td>pipeline_transform</td><td>A</td>
<td>csv_transform</td><td>pipeline</td>
<td class="pass">PASS</td><td>0</td>
<td class="pass">PASS</td><td>1</td>
</tr><tr>
<td>log_parser</td><td>pipeline</td>
<td class="pass">PASS</td><td>0</td>
<td class="pass">PASS</td><td>1</td>
</tr><tr>
<td>pipeline_transform</td><td>pipeline</td>
<td class="pass">PASS</td><td>1</td>
<td class="pass">PASS</td><td>2</td>
</tr><tr>
<td>process_exit_codes</td><td>A</td>
<td>pipeline_word_freq</td><td>pipeline</td>
<td class="pass">PASS</td><td>0</td>
<td class="pass">PASS</td><td>1</td>
</tr><tr>
<td>process_exit_codes</td><td>process</td>
<td class="pass">PASS</td><td>3</td>
<td class="pass">PASS</td><td>1</td>
</tr><tr>
<td>reverse_string</td><td>A</td>
<td class="pass">PASS</td><td>1</td>
<td class="pass">PASS</td><td>1</td>
</tr><tr>
<td>two_sum</td><td>A</td>
<td class="pass">PASS</td><td>1</td>
<td class="pass">PASS</td><td>1</td>
</tr><tr>
<td>csv_transform</td><td>B</td>
<td class="pass">PASS</td><td>0</td>
<td class="pass">PASS</td><td>1</td>
</tr><tr>
<td>env_path_builder</td><td>B</td>
<td class="pass">PASS</td><td>0</td>
<td class="pass">PASS</td><td>1</td>
</tr><tr>
<td>log_parser</td><td>B</td>
<td class="pass">PASS</td><td>0</td>
<td class="pass">PASS</td><td>1</td>
</tr><tr>
<td>pipeline_word_freq</td><td>B</td>
<td class="pass">PASS</td><td>0</td>
<td class="pass">PASS</td><td>1</td>
</tr></tbody>
<tfoot><tr>
<td><strong>Total</strong></td><td></td>
@@ -102,47 +102,61 @@
</tr></tfoot>
</table>
<h2>Per-Category Summary</h2>
<table>
<thead><tr>
<th>Category</th>
<th>Bash Pass</th><th>Lush Pass</th>
<th>Bash Avg Turns</th><th>Lush Avg Turns</th>
<th>Bash Avg Score</th><th>Lush Avg Score</th>
</tr></thead>
<tbody><tr>
<td>algorithm</td>
<td>3/3</td><td>3/3</td>
<td>1.0</td><td>1.0</td>
<td>3.6</td><td>4.1</td>
</tr><tr>
<td>environment</td>
<td>2/2</td><td>2/2</td>
<td>3.0</td><td>1.5</td>
<td>3.1</td><td>3.7</td>
</tr><tr>
<td>filesystem</td>
<td>1/2</td><td>2/2</td>
<td>2.5</td><td>2.0</td>
<td>3.2</td><td>3.9</td>
</tr><tr>
<td>pipeline</td>
<td>4/4</td><td>4/4</td>
<td>1.0</td><td>1.2</td>
<td>3.6</td><td>4.0</td>
</tr><tr>
<td>process</td>
<td>1/1</td><td>1/1</td>
<td>3.0</td><td>1.0</td>
<td>3.8</td><td>4.3</td>
</tr></tbody>
</table>
<h2>Questionnaire Scores</h2>
<div class="chart"><img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAABJAAAAKECAYAAABYR8CeAAAAOnRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjEwLjgsIGh0dHBzOi8vbWF0cGxvdGxpYi5vcmcvwVt1zgAAAAlwSFlzAAAXEgAAFxIBZ5/SUgAAnQNJREFUeJzs3QmcTfX/x/GPfd/HWiSUtRIKJWuhxVa2NhSR9uJXESpaqBSlQilESUpoU/ZSqbRSluxrDNl35v94f3+/c/93Zu6c2WfuzLyej8d93HHuOfeee+6Za+77fr6fb7aoqKgoAwAAAAAAAOKQPa4bAAAAAAAAAAIkAAAAAAAAxIsKJAAAAAAAAPgiQAIAAAAAAIAvAiQAAAAAAAD4IkACAAAAAACALwIkAAAAAAAA+CJAAgAAAAAAgC8CJAAAAAAAAPgiQAIAAAAAAIAvAiQAAAAAAAD4IkACAAAAAACALwIkAAAAAAAAECABAAAAAAAg6ahAAgAAaaZixYqWLVs2mzhxIkf9f3r06OGOia6BzGjRokXuHNcF4cV7XfQaAUB8CJAAAEhhx44ds3Hjxtl1111nFSpUsHz58lmRIkWsevXq1qdPH1uyZEmmO+YKhJ544gk+hGRiy5Yts169ernzuFChQpYnTx4rX7681atXz26//XZ3DmzZsiW9dzPLBI6hLgULFrSaNWta3759bcWKFem9q1lWcGBGMAMgM8mZ3jsAAEBm8tVXX7kP01u3bg0sK1y4sB0/ftxWrVrlLuPHj7c2bdrYpEmTrFixYpYZKDxYvHix+7lp06Zxrle5cmXLmzevC9TwX2XLlrWqVau663AUFRVlDz74oI0ePTqwTB+MixYtart373bn+vLly+3tt9+27t27U12WRrJnz24lS5YM/Pv06dO2Z88e+/PPP93ljTfecEF2z54902qXAACZHBVIAACkkPfff9+uueYa94H6rLPOsjfffNP27t1r+/fvd1VJf/31lz3wwAOWM2dOmzNnjl122WXuA19WMn/+fBeidejQIb13JWw8++yz7pjoOhy99NJLgfCoXbt29u2337rzWee2rtevX28TJkyw1q1bW44cOdJ7d7MMVX/t3LkzcFGYp6D6888/t0qVKrlA6a677rKNGzem964CADIJAiQAAFKAwiFVHp06dcouuOAC++WXX9w3/8EVRtWqVXMfxmfNmmW5c+d2oYEqNoBwpeqjF1980f2sgOjjjz+2hg0buvPXc+6557pzX8HFmDFj0nFvkStXLvc6qbpRTpw44V4XAABSAgESAAApYNCgQXbkyBHXF+aDDz6INrQkJlUpaX359NNPbd68eUlqOBtfj40zZ87Y1KlT3eOVLl3afejXfrVs2dLee+89Fw6EohBMw+w0FC0iIsJ9KC1RooQbZtWlSxdXbRI8dE374A1fe/LJJ2P1ZQmugIivibaqJt566y1r3ry5e2wdT1VzderUybeXiPZV96s+THpeGr5Tv359N3xQ/XoUekyZMiXO7YP3Sx+6n3/+ebvooousQIECbrid9ueLL76Ic/sNGzbYiBEj3If3888/322nfjQ1atRwVWebN29OUhPt4Od18uRJGzlypOs5pOFjoV579b3p3bu3nXfeeZY/f363DxdeeKE99thjFhkZaYmlbbZt2+Z+btu2bbzrq99XXFRtN3ToUPe6FC9e3A1l1HHX+fj666+7Sr1QPvroI9dPzDuHda1/z5w5M0HHVOeDqgEbNWrkzuNQ55/OUb1O6h+kY6Zjp8D3/vvv933tFALreOs11zZ6TqoMatCggQ0cONDdnh5q164d+PnQoUOxbtd7ld4DunXr5tbV+4J+18qVK2ft27ePN3RK7vP++++/XeiobfS4Z599tt1xxx2Bcy0xVBWn1/T666/3XW/dunWB96Svv/462m1z585122s/dI7pfUNVXDo3X3jhBVdtl9p0DoZ6z4zJ7z3033//tSFDhlidOnXcc9BzKVOmjHsPuPPOO10FqJ+DBw+6/5t07ut3Wb8v+l1T/zMAcKIAAECybN++PSp79uxKY6J69OiRoG0OHjwYVahQIbdNu3btot22cOFCtzy+/6a9dbR+THv27Ilq3LhxYB1dihQpEu3fbdu2jTp+/Hi07U6dOhV11VVXxdouT5480ZZ5pk2bFlW6dOmoXLlyueUFChRw/w6+bN68ObD+Oeec49Z7++23Y+3zvn37opo2bRp4jBw5ckQVLVo0Klu2bIFl/fv3D3ksmjRp4m4fNGiQO576OWfOnFGFCxeOtt9DhgwJub23X6+88kpU/fr13c96TgULFgxsq/2YMGGC7+Prkjt37qgSJUoEzgnvGH799dcht+3evbtbR9dx3e8jjzwSddlllwWeV7Fixdz+BL/2I0aM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 truncated
<h2>Agent Turns (Category A)</h2>
<div class="chart"><img src="data:image/png;base64,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 truncated
<h2>Questionnaire Scores by Category</h2>
<div class="chart"><img src="data:image/png;base64,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 truncated
<h2>Agent Turns (Solve Mode)</h2>
<div class="chart"><img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAA/gAAAKFCAYAAAB4EEI8AAAAOnRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjEwLjgsIGh0dHBzOi8vbWF0cGxvdGxpYi5vcmcvwVt1zgAAAAlwSFlzAAAXEgAAFxIBZ5/SUgAAnkJJREFUeJzt3Qm8TdX7x/FlnlOUKbMMURJJZlGaflJSUpoMTRoNzYNCJWmi0iQV0WSMUhEyZKgQZcgQmRWFZDz/13f9fuv89z3Ove5wxn0/79frdq9zzr33tNfde69nrWc9K0cgEAgYAAAAAACQ1HLG+w0AAAAAAICsI8AHAAAAAMAHCPABAAAAAPABAnwAAAAAAHyAAB8AAAAAAB8gwAcAAAAAwAcI8AEAAAAA8AECfAAAAAAAfIAAHwAAAAAAHyDABwAAAADABwjwAQAAAADwAQJ8AAAAAAB8gAAfAAAAAAAfIMAHAAAAAMAHCPABAEBMrVu3zuTIkcN+6Otkc+DAAVOlShWTL18+s2HDBpMokv24JpNoHes9e/aYk046yZxwwgnmjz/+iNjPBZB9EOADQBg7d+40BQoUCHbgVq1a5evjNH36dNOnTx8zfPjwTH2/O06Z+cjs70xmixYtssf7xRdfjPdbMT///LO56667zBlnnGGKFi1q8ubNa8qUKWPOPPNMc+2115qhQ4ealStXxvttJpTBgwebNWvWmK5du5py5cqFfQ3HNf0qVqwYvB4osP3333/TfP2WLVtMnjx5gt/TokUL4xeFCxc2PXv2NLt27TJPPvlkvN8OgCREgA8AYYwcOTJFJ3PYsGG+D/CfeOKJTAfbJUuWDPtRqFChY75GAynZMcDX8Y53gD9w4EAb2CtgXbJkifn7779tmym40Hv84IMPzG233WZuvvnmuL7PRPLnn3+afv362dn7Bx98MOxrOK6Zp7+9sWPHpvmad9991xw6dMj41R133GFOPPFE89prr5lff/013m8HQJIhwAeAMN5++237+c477wx2KA8fPsyxSmNGLdxHr169jvmaDh06cFzjYMyYMea+++6zgVKzZs3Ml19+afbt22ezV/755x/z+++/m1GjRpn27dvbWX381xtvvGGD0DZt2piyZctyXCM8ky/vvPNOmq9zA5Hu9X6jWXxlzxw8eDDug4AAkg8BPgCE+OGHH+zs5fHHH2+effZZU6lSJbN582YzefJkjhV8Y9CgQfbzaaedZqZOnWrOP/98kz9//uDzJ598srn66qvNxx9/bMaPHx/Hd5o4AoGADfClU6dOYV/Dcc28K664wmaQ6O9x/fr1YV8zZ84cs3z5cntd1sCUX7m/rxEjRtgBNwBILwJ8AEhl9l4zywp4rr/++gyl6SsYatmypR0g0EyMUqA1UKDZGK27PtaaURVsuueee0ytWrXs9xcsWNDUqFHD3H333al2ejWjpZ/rZrS+//57c9VVV5nSpUvbVOLKlSubHj162NnZ0N+l71O6uMyYMSNma+S1LMD9jrS41+j1aX3/jz/+aGe9NKuq9bnuGGf22HjNmzfP/mwFFfqbUBBSoUIF07x5c9O3b187251eei833XST/fq333476njrbySU/t/0d6jfqd+vdcqNGjWys3v79+83maFBLLn44otN7ty503xtWssoVq9ebdP4q1atal933HHHmbp169r1w0r5zwj9jesY6PuPVYhMbaDXvv/++0c9v3v3bvPMM8+Yhg0bmmLFitl21lp5DVjMnTvXZNbXX39t1q5da89tHbdwOK6Zp+vdlVdeaY4cOWKzpsJx1+Ebb7zxmNeOrP59bty40dxyyy32b0d/Q7q26NxNb9q8ijG++uqr5txzz7Up98qEKVWqlGnbtq35/PPP0/zes846y77nv/76y3z44Yfp+n0AYAUAAEH79u0LHH/88QFdHmfPnm0fW716dSBHjhyB3LlzB7Zs2ZLm0erZs6f9Xvehn6Xv09fNmjULPPTQQ/br5s2bh/3+ESNGBPLlyxf8fn1doECB4L+LFCkSmDJlylHf984779jnK1SoEBg5cmQgT5489t9FixYN5MyZM/j9tWrVCuzevTv4fevXrw+ULFkyUKhQIfu8vk//9n6MHj06038hjz/+ePB3h/rmm29Sfc7LvUavT+37P/nkk+D/83HHHRfInz9/8Bhn9tg4w4cPt+3vbRP9Dm8763ekl46p+379/tDjPXDgwBSvf/7551P8fr1v9/+gj9q1awc2bdoUyKiCBQva77/mmmsCmfXhhx+m+HvV36f33+XKlQv8/PPPR33f2rVrg6/R186CBQuCjy9dujTV36s20WsKFy4c2LNnT4rnfvzxx0DZsmWDPydXrlz2fbl/61g+9dRTmfr/7dGjh/0ZF1xwQaqv4bhmnM5NHVddL2bMmGG/rly5cuDIkSMpXrd3717bljpv1q1bF7jhhhvSvJ5m9u9Tvv/++8AJJ5wQfK2uw/p7c9cY/exwf8OO3p+uKd6/O5273uvGrbfemuZx6dKli33dVVddlaHjCSB7I8AHgJAAWx2qU045JcVxadq0qX382WefTfV4jRo1KthxU9D0+++/BwcN3njjDRt0ug5juA7pl19+aTuuGhC47777bKdRHVx9LF++PHDllVcGO5e//fZbiu91QayCC3Vgu3btaoN31ykeMmRIMCh89NFHUw3EU+soJ3qAr473xRdfHPjll1+Cz69cuTLLx8YFFHquU6dOgV9//TX4nALLhQsXBnr37h2YNGlSho6Ld9AhLRMnTgz+P7Zt2zawZs0a+/j+/fsD7733XvC9NWrUKHDo0KEMvYcWLVrY79XfmwY+Dh8+nKHvVwDkjlvjxo0DS5YssY/r50yYMCFQunRp+1yVKlWOGjhJLcCXmjVr2sfvv//+VH93q1at7Guuv/76FI9roKNEiRL2uXbt2tn2OXDggH1u69attn3dgNvYsWMDGXXWWWeleg45HNexWQrwRdffcOe8G9g577zz7L/TCvCz8vf5999/B8qXL2+f12ddm91gw5w5c2zg7gaCw/0N69pQo0YN+5z+HqZPnx74999/7XO7du2yg3ZusODFF19M9bjovqHXlCpVKsPHFED2RYAPAB7nnnuu7VA9+eSTKY7Lm2++aR9Xpy0cdf5cp/T8888/aubJG9SF65Cq01m1alX73Ouvv55qm1x66aX2NXfffXeqP1ud3rRmH0MHL/wQ4J999tmpBrhZOTbz5s2zjyvD4eDBg4FISW+Af+qpp9rXaYAp3P+fAhX3//bxxx9n6D0o6HDBrgsiNFOoQaxp06YdNTMe6sILLwweMw2EhPrhhx+CPz80KyGtAP/pp5+2j2sWPtyggwbOXObF119/neK5zp07HzMrQcGVXnPGGWcEMkKDKsoGcBkjqeG4Zuy4hgvw+/XrF3YAR1lQelwDUscK8LPy9zlgwAD7eN68ecPO8G/evDnF7H7o37DuH+59uQGmUGPGjLGvOfHEE1O9tngzWtzgHgAcCwE+APyPS8XXR2iH7a+//gqmyrvU/dDOYmqBqKOg380KhXZIXbCqzl5aM6kKLMINNHiD2FWrVoX9Xpf6qo/QDm+yB/hpBbdZOTYrVqywj2km8FjLMyId4C9evDj4nsIty3A0uOFmrDNKAXL16tWDv8f7of9nZUXo2ITauXNncNlAWgNSGjDQa+rWrZvuAF/ZFakF8N7gK3QAQJkyypLRczp2qdmxY0fwd2ekTTds2BD8vnDHxIvjuiVLAb6Otf4GlHWj2XRR9oz+5jRzrrZOK8DP6t/nmWeeaR+/9tprU/3eBx98MNW/Yff/M378+FS/X/cDt1Tnu+++C/sa73kya9asVH8WAHhRZA8A/kdbMymebNq06VHbL6kw02WXXZaiCF9o5X1RcTcVPwtHBaFUlC2c2bNn288qqFSmTBlbiCncR7du3YLF2cJRQbFTTjkl7HP6uU5aBeWSUePGjY/5mswcmypVqtgChyqQ2KBBAzNgwABbRC0WWyYuXLjQflYBvNT+bkTV772vz4hWrVqZn3/+2RYs1J7uKg6p4yT6f9bOEfrdjz322FF/7/8dezHmvPPOO+Z7W7Jkif156aGCZq5AYrgCeu4xFT3MmfP/uzEqnvjvv//ar1u3bp3qOaTilU5q51E427dvD37tjlFqOK7pP67hqJid/nZUPd4VmHPXZxVK9O72EE5W/j5VGO+nn36yX+t8SE1qz6kwn/u76tKlS6p/hyryqWKRx7qeh/v7A4C0EOADgDG2arOrFu+q5oe64YYb7OePPvoo2DEL7XwVL148zT3DtfVYOJs2bbKf1cncunVrqh8u+NR+5eEUKVIk1d/trZSe3mArWZQoUeKYr8nMscmVK5cZPXq0rZ6vTvgDDzxgzjzzTDvgo+Dgtddei9oWVtu2bbOfVX1bFbxT4/Zid6/PKAXJCuKfeuopuz3ZH3/8YX755Rcb1KtSvWingM8+++yo95bW37T3vR06dMj8+eef6X5P7hz89NNPUxxfDa4sXbo0xWtCzyFJ6xzSh5ORtnODB5JWezgc16zp3LlzMLDX9fm9995L8XhasvL3qc/6d3q/N5T373DHjh1p/h3q/yutv0Pv7hXevz8ASAsBPgAYY6ZMmRLc6qxr165HbV2mjwsvvNA+r+BeQX446dm2KRw3I6xZ4v8tnzrmB0yKQDxatM2h9t1WsHnzzTfbfeM1wKIt026//XY7w+9m/PxC/0/aOnHChAnBv+m33norpvuha3tInWtjx449avZe25zVrFkzxfd4syrUPuk5h9LarjKUBu+ymgHDcU0/bSWn7SC17/0rr7xiNmzYYLMv6tevbxKZ9+9QA2Xp+TvUln/heAfFvH9/AJAWAnwASCXtPiOvP+mkk4IzNkrxTI3SN8NRymZGU4aTnXfWPLXZKS1ZSATKymjXrp15/fXXbTCvjI2hLine truncated
<h2>Score Difference Heatmap (Lush - Bash)</h2>
<div class="chart"><img src="data:image/png;base64,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 truncated
<div class="chart"><img src="data:image/png;base64,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 truncated
<h2>Per-Category Breakdown</h2>
<h3>algorithm</h3><div class="chart"><img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAA4AAAAHxCAYAAAA81dIaAAAAOnRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjEwLjgsIGh0dHBzOi8vbWF0cGxvdGxpYi5vcmcvwVt1zgAAAAlwSFlzAAAXEgAAFxIBZ5/SUgAAUjBJREFUeJzt3QmYjeX/x/HvWCJLWcaSPUtEUimlEkqWCCVL2gilhTZa/EpCFNIu2ZJKKikVoU3aLO2lkl2JGPu+zv/63L/fc/5nZs4wxowzc+7367rmOjNnec59nkfyme99f++4xMTERAMAAAAAxLwc0R4AAAAAAODYIAACAAAAgCcIgAAAAADgCQIgAAAAAHiCAAgAAAAAniAAAgAAAIAnCIAAAAAA4AkCIAAAAAB4ggAIAAAAAJ4gAAIAAACAJwiAAAAAAOAJAiAAAAAAeIIACAAAAACeIAACAAAAgCcIgAAAZEOzZ8+2uLg469Spk2VVDRo0cGNcsWLFEb2uQoUK7nUAgIxHAAQAAMeUwp1CHgDg2MsVhfcEAAAemDBhgu3cudNKly4d7aEAAP6HAAgAADJFuXLlOLMAkMUwBRQAgCxi2rRpduONN9qpp55qJ5xwguXPn99q1aplgwYNsj179qT5ODt27LD777/fTbPMmzevVa5c2QYMGGD79u075Pq66dOn26WXXmqFCxd2r6tatao7zubNm1M8t1+/fu4448ePt/nz51uLFi2saNGi7r4ff/wx4hpAPTd475UrV7rvgy89N5IxY8bY6aefbscff7yVLFnSbr755ojjCX+vN954w8455xzLly+fqz7ee++9tnfvXve8pUuX2tVXX23Fixd3jzds2NB+/vnnNJ9bAMjuCIAAAGQRXbp0sbffftuKFClizZo1s3r16tlff/1l//nPf+yyyy6zAwcOHPYYCoqNGjWyxx9/3LZt2+aCmQLlY489Zu3atUv1dYMHD7bmzZu75jK1a9e21q1bu+mbOs65555r//77b8TXzZkzxy688EIXvBo3bmwXXXSR5cgR+Z8XCqI33HCD+17hVt8HX02bNk3xfAW32267zU466SR3PhITE23UqFHWsmVL930kTz/9tF177bVWqFAhd0wFv6FDh1q3bt1s8eLFdt5557mAevHFF7vx6PMqBKb2+QAg5iQCAIAs4d13303cuXNnkvu2bt2a2KJFC6WdxJdffjl0/2effebuu+GGG5I8f8CAAe7+OnXqJG7atCl0//LlyxPLli3rHkv+v//58+cn5siRI7FAgQKJc+fODd2/e/fuxLZt27rnt2nTJslrHn744dCxHn/88Yifp379+u5xvXc43Ve+fPlUz4Me03NKliyZ+Mcff4TuX79+fWLlypXdY5988knE99JnWLBgQej+NWvWJJYoUSIxLi4u8dRTT028//77Ew8ePOge0+11113nXte3b99UxwMAsYQKIAAAWUSrVq3cVMdwBQsWtCeffNJ9P3Xq1MMeY+TIke72iSeecFWwgKZ+9u3bN+JrnnvuOTt48KD16NHDVfsCefLkcY9pTO+8846rRiZXs2ZN6927t2UGTVvVNNRAfHy8de/ePVR5jOTOO++0s88+O/Szpo127NjRVQxVHe3fv39oGqpue/Xq5b7//PPPM+UzAEBWQxMYAACyEE1T1Fq8JUuWuLV8CmbBdEc9dihaV7d69WoXejQtM7n27du7qZDJffHFF+72mmuuSfGY1sppaqfC51dffWUdOnRI8rimmGbWnn163+ROOeUUd7tmzZo0v6ZixYqhdYK5c+eO+FhqxwOAWEMABAAgC1DIUzVK1b7U1rdpTd+hBCGmbNmyER9XNVFVweRNVP755x93m9refMH9CpfHstNnmTJlIn4GSa0pTqQtJwoUKHDYx46kyQ4AZGdMAQUAIAtQ58rhw4e70DN58mQXttTAJJi6KKkFw8x2qAqfuoVmltSayaT3Nek5HgDEGv4mBAAgC9AaO3nhhResTZs2VqpUqdB0xWXLlqXpGOqWKZHW6gUVxEhbKOi9gimkkQTbOLChOwBkfwRAAACygE2bNqU67fHNN99M0zHKly/vQtratWvt66+/TvH4W2+9FfF12m5CXn/99RSPrV+/3mbOnOmqgBdccIFlBAXb/fv3Z8ixAABHhgAIAEAWEDQ30T534VM91aBF+9ilVdAl85577rEtW7aE7ld1Tx0wI9Fee5oe+cwzz9i3334bul9TUNUZdNeuXXbllVemurbwSKniqH33IlUjAQCZiwAIAEAW0LNnT7c5+ogRI+y0006zq6++2m2qXr9+/VCoSwttyaDNzufOnWuVKlWytm3buo3Ta9SoYbVq1XJNW5J3wqxTp47bcmHr1q1Wt25du/TSS937a6N0rU2sUqWKPf/88xn2WTUeVQDPOusst2l7165djyjkAgDSjwAIAEAWqQCq+nb55ZdbQkKCvffee7Z9+3Z78cUXjygcae++jz76yO69914XKHWchQsXuoqgwpwqb0WLFk3xuj59+tgHH3zgAueCBQtsypQp7lg6zrx586xEiRIZ9lkHDx5st99+uwuBGtPYsWNt2rRpGXZ8AEDq4rQb/CEeBwAAMUJVQVX4mjZtah9++GG0hwMAiAIqgAAAxJgffvjBbSAfTp1Eb775Zve9pl0CAPxEBRAAgBhTrVo11wCmZs2abrrnqlWr7LvvvnP7CWr93bvvvnvIvf0AALGLAAgAQIxRwxZtHbFo0SLbuHGj26y9evXqrvKnhjK5cuWK9hABAFFCAAQAAAAAT7AGEAAAAAA8QQAEAAAAAE8QAAEAAADAEwRAAAAAAPAEARAAAAAAPEEABAAAAABPsBEQYlbJkiVtx44dVq5cuWgPBQAAAMgwq1atsvz589vatWuP+LVUABGzFP727t0b7WEgCg4cOOC+4B+uvb+49n7iuvvL92u/b98+92/d9KACiJilyp/+Yli4cGG0h4JjLCEhwd3Gx8dz7j3DtfcX195PXHd/+X7ta9Soke7XUgEEAAAAAE8QAAEAAADAEwRAAAAAAPAEARAAAAAAPEEABAAAAABPEAABAAAAwBMEQAAAAADwBAEQAAAAADxBAAQAAAAATxAAAQAAAMATBEAAAAAA8AQBEAAAAAA8QQAEAAAAAE8QAAEAAADAEwRAAAAAAPAEARAAAAAAPEEABAAAAABP5Ir2AIDMtH/9elvVoSMn2TPbihdztzvXrY/2UHCMce39xbX3E9c9c5SbNDGTjoysgAogAAAAAHiCAAgAAAAAniAAAgAAAIAnCIAAAAAA4AkCIAAAAAB4ggAIAAAAAJ4gAAIAAACAJwiAAAAAAOAJAiAAAAAAeIIACAAAAACeIAACAAAAgCcIgAAAAADgCQIgAAAAAHiCAAgAAAAAniAAxpDZs2dbXFycuz1SnTp1sgIFCqTpuXqPfv36hX4eP368u2/FihWh+xo0aOC+AAAAAGQdBMCjEASf4CtXrlxWunRpF6ZWr16dcVcpBvzzzz8uNP7444/RHgoAAADgrVzRHkAs6N+/v5188sm2e/dumzt3rguGX375pf3666+WN29eizW7du1yYfdQZs2alSIAPvLII1ahQgU744wzMnmEAAAAACIhAGaAZs2a2dlnn+2+79q1q8XHx9vjjz9u7733nrVr185iTVpC7XHHHXdMxgIAAAAg7ZgCmgnq1avnbpcuXRq6748//rCrrrrKihQp4gKUAqMCYriNGzdar169rGbNmm493gknnODC5U8//ZTiPf7++29r3bq15c+f34oXL2533XWX7dmzJ8XzvvjiC2vbtq2VK1fO8uTJY2XLlnXPVRUvkmXLllmTJk3ccUuVKuWqm4mJiYdcAxhJ+BpArUk855xz3PedO3cOTZlVpfThhx+23Llz2/r161Mc46abbrJChQq5yioAAACAo0cFMBMEzVAKFy7sbhcuXGgXXHCBWx94//33u3D15ptvugD39ttv2xVXXBEKX++++64LbJpS+u+//9qLL75o9evXt99++80FMlF4u+SSS2zVqlXWs2dPd/8rr7xin376aYqxvPXWW7Zz50675ZZbrGjRojZ//nx79tlnXYDUY+EOHDhgTZs2tfPOO8+GDBliM2bMcAFt//79Lgim16mnnupe37dvXxfqgoB8/vnn24UXXugee+ONN+z2228PvWbv3r02efJka9OmTUxOowUAAACigQCYAbZs2WIJCQmuUjVv3jy31k3VthYtWrjH77jjDleBW7Bggbtfbr31Vhd+7rvvvlAAVOXvzz//tBw5/r8we91111m1atVs7Nix9tBDD7n7Ro0a5Z6nEKmwKN26dbNatWqlGJumoh5//PGhnxXAKleubH369HEBUuMKaPwKgM8880xojJdffrk7hoKmpramR4kSJVwlUwGwbt26du211yZ5XPe9+uqrSQLgtGnTbNOmTe7zH06NGjUi3q8KbJn8+dM1ZgAAACAWEQAzQKNGjZL8rEYnCjRlypRx0zpVmVOVa9u2be4roKmWqrCpY6iqg0E4DKpxmzdvdlNBq1atat9//33osenTp9tJJ53kppQG8uXL58Ldvffem2Qs4eFvx44drnqoypumdf7www9JAqCEhzBN09TPCmMff/yxdejQwTLD9ddf7yqUCmyVKlVy97322mtuuqqqn0djY6581qNiywwaKbKLkgX/+0uUtQUORnsoOMa49v7i2vspu133qYMy599SwJEgAGaA559/3k455RRXCRw3bpzNmTMnFOaWLFniwpaqd0EFL7l169a5AHjw4EF7+umnbcSIEbZ8+XIXAgOavhlYuXKlq+IpoIVTUExOVT5V3rTeUBW1cBpvOFUeK1asmOQ+fS4J3+Mvo7Vv397uvPNOF/o0Vo3rgw8+cGsVk3/GSDTFNrXK4JqErZkwYgAAACLine truncated
<h2>Per-Task Detail</h2>
<div class="task-detail">
<h3>env_config <span class="cat">[a]</span>
<span class="pass">bash=PASS</span>
<span class="pass">lush=PASS</span>
</h3>
<table class="scores">
<thead><tr><th>Metric</th><th>Bash</th><th>Lush</th><th>Diff</th></tr></thead>
<tbody><tr><td>Readability</td><td>3</td><td>4</td><td class="pos">+1</td></tr><tr><td>Expressiveness</td><td>3</td><td>4</td><td class="pos">+1</td></tr><tr><td>Conciseness</td><td>2</td><td>3</td><td class="pos">+1</td></tr><tr><td>Error handling</td><td>2</td><td>3</td><td class="pos">+1</td></tr><tr><td>Overall preference</td><td>3</td><td>4</td><td class="pos">+1</td></tr><tr><td>Learning curve</td><td>2</td><td>4</td><td class="pos">+2</td></tr></tbody>
</table>
<div class="observations"><p><strong>bash:</strong> Bash&#x27;s regex matching and array handling work well for this task, but the syntax is arcane (${!key} for indirect variable access, BASH_REMATCH array). The printf-pipe-sort pattern for array sorting is a common bash idiom but not intuitive. Error handling would require additional validation that bash doesn&#x27;t make easy. Good for system administration contexts where bash is expected, but the syntax barriers make it less accessible than higher-level languages.</p>
<p><strong>lush:</strong> Lush combines Lua&#x27;s familiarity with shell-like conveniences effectively. The backtick syntax for command execution and built-in functions like lush.envset feel natural. However, the solution required more manual string processing than ideal - having to parse env output and match keys could be more elegant with better built-in environment variable introspection.</p>
</div>
</div>
<div class="task-detail">
<h3>file_organizer <span class="cat">[a]</span>
<span class="fail">bash=FAIL</span>
<span class="pass">lush=PASS</span>
</h3>
<table class="scores">
<thead><tr><th>Metric</th><th>Bash</th><th>Lush</th><th>Diff</th></tr></thead>
<tbody><tr><td>Readability</td><td>3</td><td>4</td><td class="pos">+1</td></tr><tr><td>Expressiveness</td><td>2</td><td>5</td><td class="pos">+3</td></tr><tr><td>Conciseness</td><td>2</td><td>4</td><td class="pos">+2</td></tr><tr><td>Error handling</td><td>2</td><td>4</td><td class="pos">+2</td></tr><tr><td>Overall preference</td><td>2</td><td>4</td><td class="pos">+2</td></tr><tr><td>Learning curve</td><td>2</td><td>4</td><td class="pos">+2</td></tr></tbody>
</table>
<div class="observations"><p><strong>bash:</strong> Bash required several workarounds and awkward constructs: dynamic variable creation with eval (security risk), manual string building for output sorting, complex parameter expansion syntax, and careful escaping. While bash excels at file operations, the lack of proper data structures made this solution verbose and harder to maintain compared to higher-level languages.</p>
<p><strong>lush:</strong> Lush&#x27;s shell command interpolation with backticks and the seamless integration with Lua made this file organization task very natural to implement. The ability to execute shell commands inline while maintaining full Lua data structures for processing was particularly elegant for this type of system administration task.</p>
</div>
</div>
<div class="task-detail">
<h3>fizzbuzz <span class="cat">[a]</span>
<h3>fizzbuzz <span class="cat">[algorithm/solve]</span>
<span class="pass">bash=PASS</span>
<span class="pass">lush=PASS</span>
</h3>
@@ -156,49 +170,7 @@
</div>
<div class="task-detail">
<h3>multi_file_search <span class="cat">[a]</span>
<span class="pass">bash=PASS</span>
<span class="pass">lush=PASS</span>
</h3>
<table class="scores">
<thead><tr><th>Metric</th><th>Bash</th><th>Lush</th><th>Diff</th></tr></thead>
<tbody><tr><td>Readability</td><td>4</td><td>4</td><td class="">0</td></tr><tr><td>Expressiveness</td><td>5</td><td>4</td><td class="neg">-1</td></tr><tr><td>Conciseness</td><td>5</td><td>3</td><td class="neg">-2</td></tr><tr><td>Error handling</td><td>3</td><td>4</td><td class="pos">+1</td></tr><tr><td>Overall preference</td><td>5</td><td>4</td><td class="neg">-1</td></tr><tr><td>Learning curve</td><td>3</td><td>3</td><td class="">0</td></tr></tbody>
</table>
<div class="observations"><p><strong>bash:</strong> Bash excels at text processing and file operations like this. The pipeline approach with grep, sed, and sort feels very natural. However, the syntax can be cryptic for newcomers (especially the sort flags), and proper error handling would require additional complexity that might hurt readability.</p>
<p><strong>lush:</strong> Lush combines Lua&#x27;s simplicity with shell command integration nicely. The backtick syntax for command execution is intuitive, and the Lua-based file I/O and string processing felt natural. However, the file globbing required some manual parsing and filtering that felt a bit verbose compared to dedicated shell scripting. Overall, it strikes a good balance between scripting convenience and programming language features.</p>
</div>
</div>
<div class="task-detail">
<h3>pipeline_transform <span class="cat">[a]</span>
<span class="pass">bash=PASS</span>
<span class="pass">lush=PASS</span>
</h3>
<table class="scores">
<thead><tr><th>Metric</th><th>Bash</th><th>Lush</th><th>Diff</th></tr></thead>
<tbody><tr><td>Readability</td><td>4</td><td>4</td><td class="">0</td></tr><tr><td>Expressiveness</td><td>5</td><td>4</td><td class="neg">-1</td></tr><tr><td>Conciseness</td><td>5</td><td>3</td><td class="neg">-2</td></tr><tr><td>Error handling</td><td>3</td><td>4</td><td class="pos">+1</td></tr><tr><td>Overall preference</td><td>5</td><td>4</td><td class="neg">-1</td></tr><tr><td>Learning curve</td><td>3</td><td>4</td><td class="pos">+1</td></tr></tbody>
</table>
<div class="observations"><p><strong>bash:</strong> Bash excels at pipeline transformations like this - the pipe operator creates a natural flow that mirrors the logical steps. The regex in sed might be intimidating to newcomers, but the overall structure is intuitive. Error handling is implicit (pipes fail if any command fails) but lacks granular control.</p>
<p><strong>lush:</strong> Lush felt very much like Lua, which made the text processing task straightforward. The pattern matching and string manipulation functions were intuitive. However, the manual deduplication logic required more verbose code than some other languages might need. Overall, it provided good control and clarity for this pipeline transformation task.</p>
</div>
</div>
<div class="task-detail">
<h3>process_exit_codes <span class="cat">[a]</span>
<span class="pass">bash=PASS</span>
<span class="pass">lush=PASS</span>
</h3>
<table class="scores">
<thead><tr><th>Metric</th><th>Bash</th><th>Lush</th><th>Diff</th></tr></thead>
<tbody><tr><td>Readability</td><td>4</td><td>5</td><td class="pos">+1</td></tr><tr><td>Expressiveness</td><td>4</td><td>5</td><td class="pos">+1</td></tr><tr><td>Conciseness</td><td>5</td><td>5</td><td class="">0</td></tr><tr><td>Error handling</td><td>3</td><td>3</td><td class="">0</td></tr><tr><td>Overall preference</td><td>4</td><td>4</td><td class="">0</td></tr><tr><td>Learning curve</td><td>3</td><td>4</td><td class="pos">+1</td></tr></tbody>
</table>
<div class="observations"><p><strong>bash:</strong> Bash is well-suited for this task since it naturally handles command execution and exit codes. The main challenges are bash-specific syntax quirks like the read loop condition and variable arithmetic. The use of eval introduces potential security concerns but is necessary for dynamic command execution. Overall, bash feels like the right tool for this system administration type task.</p>
<p><strong>lush:</strong> Lush combines the simplicity of shell scripting with Lua&#x27;s readability. The command execution syntax `${command}` is intuitive and the ability to access exit codes via .code is clean. The Lua-based control structures and string handling make it much more readable than pure bash while maintaining the shell&#x27;s natural command execution feel.</p>
</div>
</div>
<div class="task-detail">
<h3>reverse_string <span class="cat">[a]</span>
<h3>reverse_string <span class="cat">[algorithm/solve]</span>
<span class="pass">bash=PASS</span>
<span class="pass">lush=PASS</span>
</h3>
@@ -212,7 +184,7 @@
</div>
<div class="task-detail">
<h3>two_sum <span class="cat">[a]</span>
<h3>two_sum <span class="cat">[algorithm/solve]</span>
<span class="pass">bash=PASS</span>
<span class="pass">lush=PASS</span>
</h3>
@@ -226,21 +198,21 @@
</div>
<div class="task-detail">
<h3>csv_transform <span class="cat">[b]</span>
<h3>env_config <span class="cat">[environment/solve]</span>
<span class="pass">bash=PASS</span>
<span class="pass">lush=PASS</span>
</h3>
<table class="scores">
<thead><tr><th>Metric</th><th>Bash</th><th>Lush</th><th>Diff</th></tr></thead>
<tbody><tr><td>Readability</td><td>4</td><td>4</td><td class="">0</td></tr><tr><td>Expressiveness</td><td>4</td><td>4</td><td class="">0</td></tr><tr><td>Conciseness</td><td>5</td><td>5</td><td class="">0</td></tr><tr><td>Error handling</td><td>2</td><td>3</td><td class="pos">+1</td></tr><tr><td>Overall preference</td><td>4</td><td>4</td><td class="">0</td></tr><tr><td>Learning curve</td><td>3</td><td>4</td><td class="pos">+1</td></tr></tbody>
<tbody><tr><td>Readability</td><td>3</td><td>4</td><td class="pos">+1</td></tr><tr><td>Expressiveness</td><td>3</td><td>4</td><td class="pos">+1</td></tr><tr><td>Conciseness</td><td>2</td><td>3</td><td class="pos">+1</td></tr><tr><td>Error handling</td><td>2</td><td>3</td><td class="pos">+1</td></tr><tr><td>Overall preference</td><td>3</td><td>4</td><td class="pos">+1</td></tr><tr><td>Learning curve</td><td>2</td><td>4</td><td class="pos">+2</td></tr></tbody>
</table>
<div class="observations"><p><strong>bash:</strong> Bash excels at simple text processing tasks like this CSV transformation. The IFS mechanism and read command make parsing straightforward, and the solution is very concise. However, bash&#x27;s quirky syntax (like the `|| [[ -n &quot;$name&quot; ]]` construct for handling files without trailing newlines) can be confusing for newcomers. For more complex CSV handling with edge cases, a dedicated tool or language might be better.</p>
<p><strong>lush:</strong> Lush&#x27;s Lua-based syntax made the CSV parsing task quite natural. The pattern matching with match() worked well for simple CSV parsing, and the io functions provided clean file handling. The solution was concise and readable, though proper CSV parsing (handling quoted fields, escapes) would require more complex regex or a dedicated library.</p>
<div class="observations"><p><strong>bash:</strong> Bash&#x27;s regex matching and array handling work well for this task, but the syntax is arcane (${!key} for indirect variable access, BASH_REMATCH array). The printf-pipe-sort pattern for array sorting is a common bash idiom but not intuitive. Error handling would require additional validation that bash doesn&#x27;t make easy. Good for system administration contexts where bash is expected, but the syntax barriers make it less accessible than higher-level languages.</p>
<p><strong>lush:</strong> Lush combines Lua&#x27;s familiarity with shell-like conveniences effectively. The backtick syntax for command execution and built-in functions like lush.envset feel natural. However, the solution required more manual string processing than ideal - having to parse env output and match keys could be more elegant with better built-in environment variable introspection.</p>
</div>
</div>
<div class="task-detail">
<h3>env_path_builder <span class="cat">[b]</span>
<h3>env_path_builder <span class="cat">[environment/convert]</span>
<span class="pass">bash=PASS</span>
<span class="pass">lush=PASS</span>
</h3>
@@ -254,7 +226,49 @@
</div>
<div class="task-detail">
<h3>log_parser <span class="cat">[b]</span>
<h3>file_organizer <span class="cat">[filesystem/solve]</span>
<span class="fail">bash=FAIL</span>
<span class="pass">lush=PASS</span>
</h3>
<table class="scores">
<thead><tr><th>Metric</th><th>Bash</th><th>Lush</th><th>Diff</th></tr></thead>
<tbody><tr><td>Readability</td><td>3</td><td>4</td><td class="pos">+1</td></tr><tr><td>Expressiveness</td><td>2</td><td>5</td><td class="pos">+3</td></tr><tr><td>Conciseness</td><td>2</td><td>4</td><td class="pos">+2</td></tr><tr><td>Error handling</td><td>2</td><td>4</td><td class="pos">+2</td></tr><tr><td>Overall preference</td><td>2</td><td>4</td><td class="pos">+2</td></tr><tr><td>Learning curve</td><td>2</td><td>4</td><td class="pos">+2</td></tr></tbody>
</table>
<div class="observations"><p><strong>bash:</strong> Bash required several workarounds and awkward constructs: dynamic variable creation with eval (security risk), manual string building for output sorting, complex parameter expansion syntax, and careful escaping. While bash excels at file operations, the lack of proper data structures made this solution verbose and harder to maintain compared to higher-level languages.</p>
<p><strong>lush:</strong> Lush&#x27;s shell command interpolation with backticks and the seamless integration with Lua made this file organization task very natural to implement. The ability to execute shell commands inline while maintaining full Lua data structures for processing was particularly elegant for this type of system administration task.</p>
</div>
</div>
<div class="task-detail">
<h3>multi_file_search <span class="cat">[filesystem/solve]</span>
<span class="pass">bash=PASS</span>
<span class="pass">lush=PASS</span>
</h3>
<table class="scores">
<thead><tr><th>Metric</th><th>Bash</th><th>Lush</th><th>Diff</th></tr></thead>
<tbody><tr><td>Readability</td><td>4</td><td>4</td><td class="">0</td></tr><tr><td>Expressiveness</td><td>5</td><td>4</td><td class="neg">-1</td></tr><tr><td>Conciseness</td><td>5</td><td>3</td><td class="neg">-2</td></tr><tr><td>Error handling</td><td>3</td><td>4</td><td class="pos">+1</td></tr><tr><td>Overall preference</td><td>5</td><td>4</td><td class="neg">-1</td></tr><tr><td>Learning curve</td><td>3</td><td>3</td><td class="">0</td></tr></tbody>
</table>
<div class="observations"><p><strong>bash:</strong> Bash excels at text processing and file operations like this. The pipeline approach with grep, sed, and sort feels very natural. However, the syntax can be cryptic for newcomers (especially the sort flags), and proper error handling would require additional complexity that might hurt readability.</p>
<p><strong>lush:</strong> Lush combines Lua&#x27;s simplicity with shell command integration nicely. The backtick syntax for command execution is intuitive, and the Lua-based file I/O and string processing felt natural. However, the file globbing required some manual parsing and filtering that felt a bit verbose compared to dedicated shell scripting. Overall, it strikes a good balance between scripting convenience and programming language features.</p>
</div>
</div>
<div class="task-detail">
<h3>csv_transform <span class="cat">[pipeline/convert]</span>
<span class="pass">bash=PASS</span>
<span class="pass">lush=PASS</span>
</h3>
<table class="scores">
<thead><tr><th>Metric</th><th>Bash</th><th>Lush</th><th>Diff</th></tr></thead>
<tbody><tr><td>Readability</td><td>4</td><td>4</td><td class="">0</td></tr><tr><td>Expressiveness</td><td>4</td><td>4</td><td class="">0</td></tr><tr><td>Conciseness</td><td>5</td><td>5</td><td class="">0</td></tr><tr><td>Error handling</td><td>2</td><td>3</td><td class="pos">+1</td></tr><tr><td>Overall preference</td><td>4</td><td>4</td><td class="">0</td></tr><tr><td>Learning curve</td><td>3</td><td>4</td><td class="pos">+1</td></tr></tbody>
</table>
<div class="observations"><p><strong>bash:</strong> Bash excels at simple text processing tasks like this CSV transformation. The IFS mechanism and read command make parsing straightforward, and the solution is very concise. However, bash&#x27;s quirky syntax (like the `|| [[ -n &quot;$name&quot; ]]` construct for handling files without trailing newlines) can be confusing for newcomers. For more complex CSV handling with edge cases, a dedicated tool or language might be better.</p>
<p><strong>lush:</strong> Lush&#x27;s Lua-based syntax made the CSV parsing task quite natural. The pattern matching with match() worked well for simple CSV parsing, and the io functions provided clean file handling. The solution was concise and readable, though proper CSV parsing (handling quoted fields, escapes) would require more complex regex or a dedicated library.</p>
</div>
</div>
<div class="task-detail">
<h3>log_parser <span class="cat">[pipeline/convert]</span>
<span class="pass">bash=PASS</span>
<span class="pass">lush=PASS</span>
</h3>
@@ -268,7 +282,21 @@
</div>
<div class="task-detail">
<h3>pipeline_word_freq <span class="cat">[b]</span>
<h3>pipeline_transform <span class="cat">[pipeline/solve]</span>
<span class="pass">bash=PASS</span>
<span class="pass">lush=PASS</span>
</h3>
<table class="scores">
<thead><tr><th>Metric</th><th>Bash</th><th>Lush</th><th>Diff</th></tr></thead>
<tbody><tr><td>Readability</td><td>4</td><td>4</td><td class="">0</td></tr><tr><td>Expressiveness</td><td>5</td><td>4</td><td class="neg">-1</td></tr><tr><td>Conciseness</td><td>5</td><td>3</td><td class="neg">-2</td></tr><tr><td>Error handling</td><td>3</td><td>4</td><td class="pos">+1</td></tr><tr><td>Overall preference</td><td>5</td><td>4</td><td class="neg">-1</td></tr><tr><td>Learning curve</td><td>3</td><td>4</td><td class="pos">+1</td></tr></tbody>
</table>
<div class="observations"><p><strong>bash:</strong> Bash excels at pipeline transformations like this - the pipe operator creates a natural flow that mirrors the logical steps. The regex in sed might be intimidating to newcomers, but the overall structure is intuitive. Error handling is implicit (pipes fail if any command fails) but lacks granular control.</p>
<p><strong>lush:</strong> Lush felt very much like Lua, which made the text processing task straightforward. The pattern matching and string manipulation functions were intuitive. However, the manual deduplication logic required more verbose code than some other languages might need. Overall, it provided good control and clarity for this pipeline transformation task.</p>
</div>
</div>
<div class="task-detail">
<h3>pipeline_word_freq <span class="cat">[pipeline/convert]</span>
<span class="pass">bash=PASS</span>
<span class="pass">lush=PASS</span>
</h3>
@@ -278,6 +306,20 @@
</table>
<div class="observations"><p><strong>bash:</strong> Bash excels at text processing pipelines with its built-in tools like tr, grep, sort, and uniq. The solution is extremely concise but requires knowledge of Unix command-line tools and their options. The pipe-based approach is natural for this problem, but the syntax can be cryptic with options like &#x27;tr -cs&#x27; and &#x27;sort -k1,1rn&#x27;. The final while loop with the complex condition is necessary but makes the code less readable.</p>
<p><strong>lush:</strong> Lush feels very much like Lua with clean syntax for text processing. The gmatch function for pattern matching and the flexible table structure made the word frequency counting natural. The sorting with custom comparison functions worked smoothly. Overall, it struck a good balance between simplicity and power for this text processing task.</p>
</div>
</div>
<div class="task-detail">
<h3>process_exit_codes <span class="cat">[process/solve]</span>
<span class="pass">bash=PASS</span>
<span class="pass">lush=PASS</span>
</h3>
<table class="scores">
<thead><tr><th>Metric</th><th>Bash</th><th>Lush</th><th>Diff</th></tr></thead>
<tbody><tr><td>Readability</td><td>4</td><td>5</td><td class="pos">+1</td></tr><tr><td>Expressiveness</td><td>4</td><td>5</td><td class="pos">+1</td></tr><tr><td>Conciseness</td><td>5</td><td>5</td><td class="">0</td></tr><tr><td>Error handling</td><td>3</td><td>3</td><td class="">0</td></tr><tr><td>Overall preference</td><td>4</td><td>4</td><td class="">0</td></tr><tr><td>Learning curve</td><td>3</td><td>4</td><td class="pos">+1</td></tr></tbody>
</table>
<div class="observations"><p><strong>bash:</strong> Bash is well-suited for this task since it naturally handles command execution and exit codes. The main challenges are bash-specific syntax quirks like the read loop condition and variable arithmetic. The use of eval introduces potential security concerns but is necessary for dynamic command execution. Overall, bash feels like the right tool for this system administration type task.</p>
<p><strong>lush:</strong> Lush combines the simplicity of shell scripting with Lua&#x27;s readability. The command execution syntax `${command}` is intuitive and the ability to access exit codes via .code is clean. The Lua-based control structures and string handling make it much more readable than pure bash while maintaining the shell&#x27;s natural command execution feel.</p>
</div>
</div>
@@ -1,5 +1,6 @@
name = "fizzbuzz"
category = "a"
category = "algorithm"
mode = "solve"
description = """
Read a single integer N from stdin. Print numbers from 1 to N, one per line.
For multiples of 3, print "Fizz" instead of the number.
@@ -1,5 +1,6 @@
name = "reverse_string"
category = "a"
category = "algorithm"
mode = "solve"
description = """
Read a single line from stdin and print it reversed to stdout.
"""
@@ -1,5 +1,6 @@
name = "two_sum"
category = "a"
category = "algorithm"
mode = "solve"
description = """
Read input from stdin. The first line contains a target integer.
The second line contains space-separated integers (the array).
@@ -1,5 +1,6 @@
name = "env_config"
category = "a"
category = "environment"
mode = "solve"
description = """
Read a config format from stdin where each line is "KEY=VALUE".
For each line, set an environment variable with that key and value.
@@ -1,5 +1,6 @@
name = "env_path_builder"
category = "b"
category = "environment"
mode = "convert"
description = """
Read directory paths from stdin, one per line.
Append each to the MYPATH environment variable (colon-separated), skipping duplicates.
@@ -1,5 +1,6 @@
name = "file_organizer"
category = "a"
category = "filesystem"
mode = "solve"
description = """
You are given a working directory containing several files with extensions.
Read a list of extension-to-directory mappings from stdin, one per line, in the format:
@@ -1,5 +1,6 @@
name = "multi_file_search"
category = "a"
category = "filesystem"
mode = "solve"
description = """
You are given a working directory containing several text files.
Read a search pattern (a simple string, not regex) from stdin.
@@ -1,5 +1,6 @@
name = "csv_transform"
category = "b"
category = "pipeline"
mode = "convert"
description = """
Read CSV data from stdin. The first line is a header.
Each subsequent line has fields: name,age,city
@@ -1,5 +1,6 @@
name = "log_parser"
category = "b"
category = "pipeline"
mode = "convert"
description = """
Read log lines from stdin. Each line has the format: "LEVEL: message"
where LEVEL is one of ERROR, WARN, INFO.
@@ -1,5 +1,6 @@
name = "pipeline_transform"
category = "a"
category = "pipeline"
mode = "solve"
description = """
Read lines from stdin. Build a pipeline that:
1. Filters to only lines containing the word "error" (case-insensitive)
@@ -1,5 +1,6 @@
name = "pipeline_word_freq"
category = "b"
category = "pipeline"
mode = "convert"
description = """
Read text from stdin. Count the frequency of each word (case-insensitive, only alphabetic characters count as words).
Print the top 5 most frequent words in descending order of frequency, in the format:
@@ -1,5 +1,6 @@
name = "process_exit_codes"
category = "a"
category = "process"
mode = "solve"
description = """
Read commands from stdin, one per line. Execute each command as a subprocess.
For each command, print: "command: exit_code" where command is the original command text