Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/)
and this project adheres to [Semantic Versioning](https://semver.org/).

## [Unreleased]
### Changed
- The `claw-eval` port now uses the same `test-cases/<suite>/<task-identifier>/task.json` layout as the native corpora, instead of flat `<task-identifier>.json` files. Case discovery in `clawbench-batch` and the TUI is a plain `*/task.json` search again, and the `validate-task` workflow covers the suite without special-casing.

## [0.10.0] - 2026-08-30
### Added
Expand Down
14 changes: 2 additions & 12 deletions src/clawbench/runner/batch.py
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ def discover_models(patterns: list[str] | None, all_models: bool) -> list[str]:


def _case_id(d: Path) -> int | None:
"""Extract the numeric task ID from V1/V2/flat Claw-Eval case names."""
"""Extract the numeric task ID from V1/V2/Claw-Eval case names."""
match = re.match(r"^(?:v\d+-|ce-)?[A-Za-z]?(\d+)", d.stem)
if not match:
return None
Expand All @@ -104,16 +104,8 @@ def _resolve_cases_dir(cases_dir: str | Path) -> Path:
return path


def _flat_case_files(base: Path) -> list[Path]:
return [
p
for p in base.glob("*.json")
if p.is_file() and p.name not in {"eligibility-report.json"}
]


def _all_cases_in(base: Path) -> list[Path]:
return [p.parent for p in base.glob("*/task.json")] + _flat_case_files(base)
return [p.parent for p in base.glob("*/task.json")]


def discover_cases(
Expand All @@ -139,8 +131,6 @@ def discover_cases(
for d in expanded:
if d.is_dir() and (d / "task.json").exists():
dirs.append(d)
elif d.is_file() and d.suffix == ".json":
dirs.append(d)
elif case_range:
dirs = sorted(_all_cases_in(base), key=_case_sort_key)
else:
Expand Down
7 changes: 1 addition & 6 deletions src/clawbench/tui.py
Original file line number Diff line number Diff line change
Expand Up @@ -383,13 +383,8 @@ def _case_sort_key(case: str) -> tuple[int, int, str]:

def load_cases(cases_dir_name: str = "test-cases") -> list[str]:
cases_dir = ASSET_ROOT / cases_dir_name
flat_cases = [
p.stem
for p in cases_dir.glob("*.json")
if p.name not in {"eligibility-report.json"}
]
cases = sorted(
[*(p.parent.name for p in cases_dir.glob("*/task.json")), *flat_cases],
(p.parent.name for p in cases_dir.glob("*/task.json")),
key=_case_sort_key,
)
if not cases:
Expand Down
14 changes: 3 additions & 11 deletions tests/test_host_tasks.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,7 @@ def _suite_base(suite: str) -> Path:

def _task_files_for_suite(suite: str) -> list[Path]:
base = _suite_base(suite)
task_files = [path for path in base.glob("*/task.json") if path.is_file()]
task_files.extend(
path
for path in base.glob("*.json")
if path.is_file() and path.name != "eligibility-report.json"
)
return sorted(task_files)
return sorted(path for path in base.glob("*/task.json") if path.is_file())


@pytest.mark.parametrize("suite", sorted(batch.CASE_SUITES))
Expand All @@ -37,10 +31,8 @@ def test_builtin_case_suites_are_discoverable(suite: str) -> None:

assert cases, f"{suite} should contain at least one case"
for case in cases:
if case.is_dir():
assert (case / "task.json").is_file()
else:
assert case.is_file()
assert case.is_dir(), f"{case} should be a task directory"
assert (case / "task.json").is_file()


@pytest.mark.parametrize("suite", sorted(batch.CASE_SUITES))
Expand Down
2 changes: 1 addition & 1 deletion tests/test_tui_helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,7 @@ def test_tui_dataset_and_case_helpers_use_known_suites() -> None:
)


def test_tui_load_cases_reads_flat_and_directory_suites() -> None:
def test_tui_load_cases_reads_every_builtin_suite() -> None:
v1_cases = tui.load_cases("test-cases/v1")
claw_eval_cases = tui.load_cases("test-cases/claw-eval")

Expand Down