Parent
#13
What to build
The foundational skeleton every other slice imports. Not a full vertical slice on its own - it stands up the package, the extension points, and the shared primitives so the tracer bullet stays thin.
- A new
benchpress package at the repo root (the v2 system stays under legacy/), with a CLI stub exposing the command surface (generate, run, score, stats, export, audit) - subcommands may be stubs that exit cleanly.
- Core data types: Item, Part, PartResult, ItemResult, ModuleMeta (plain serializable dataclasses matching the PRD shapes).
- The three registries that provide extensibility (generalizing the legacy
CHECKERS dict): a module registry, a part-scorer registry keyed by part type, and a metric registry - each with a registration decorator, populated by import side-effect.
- The tagged-text extractor: parse a model response into a dict of label -> raw field, tolerant (case-insensitive labels, markdown-tolerant, last occurrence wins, extra prose ignored).
- The status-taxonomy classifier: map (stop_reason, stop_details, extraction outcome) -> one of ok | refusal | invalid_answer | truncated | api_error.
- Root config model registry reused from the existing
config.yaml shape (provider, model, company, type, launch_date, api_key_env, params).
Acceptance criteria
Blocked by
None - can start immediately
Parent
#13
What to build
The foundational skeleton every other slice imports. Not a full vertical slice on its own - it stands up the package, the extension points, and the shared primitives so the tracer bullet stays thin.
benchpresspackage at the repo root (the v2 system stays underlegacy/), with a CLI stub exposing the command surface (generate,run,score,stats,export,audit) - subcommands may be stubs that exit cleanly.CHECKERSdict): a module registry, a part-scorer registry keyed by part type, and a metric registry - each with a registration decorator, populated by import side-effect.config.yamlshape (provider, model, company, type, launch_date, api_key_env, params).Acceptance criteria
benchpressimports cleanly;pytestexits 0 at the repo root (a trivial placeholder test is fine)Blocked by
None - can start immediately