Commit History
-
submit: shard large submissions across HF Jobs + merge (UC3)
Michael Rabinovich +1: CursorSubmissions over SHARD_THRESHOLD (12) fixtures now fan out into 12-fixture shards dispatched at once, polled to terminal with ERROR-only retry, then merged into one run via write_run_summary (regenerating the report + gallery from the merged whole) before the row flips. At/under the threshold a submission stays a single job, so the original path is unchanged. Eval is CPU-bound, so more shards is the throughput lever. Co-authored-by: Cursor <cursoragent@cursor.com>293bc3b -
gallery: stop negative-caching render fetches
Michael Rabinovich +1: CursorRenders land after boot (a submission completes, or an existing row is backfilled), so lru_cache was pinning boot-time 404s and keeping thumbnails dashed until the next restart. Drop the memoization on the render byte-fetchers; hf_hub_download keeps its own per-revision disk cache, so re-fetching an unchanged thumbnail stays cheap. Also picks up the 3 baseline rows' just-backfilled renders on this redeploy. Co-authored-by: Cursor <cursoragent@cursor.com>8eb8954 -
add visual Gallery tab (top-10 verified, sticky GT, fixture picker)
Michael Rabinovich +1: CursorVisual-first leaderboard as a new default tab. Self-contained HTML (gallery.py) inlined into an iframe srcdoc with base64 thumbnails, so it works on the private Space where HF's edge 404s in-browser custom-route fetches (same constraint the report viewer's srcdoc path already handles). Top-10 verified only, teal sticky ground-truth row, 3-fixture picker (drop-oldest, global re-render), dashed cell for invalid/missing fixtures, per-row generation/editing/validity expander, and a GT-vs-output compare modal per thumbnail. Existing Leaderboard / Submit / About / Admin tabs unchanged. Co-authored-by: Cursor <cursoragent@cursor.com>01d67e9 -
docker: cache-bust the cadgenbench @main install
Michael Rabinovich +1: CursorADD the GitHub commits endpoint for the tracked ref before the pip install so BuildKit invalidates the cached layer whenever main moves; otherwise @main would stay pinned to the first build's HEAD. No-op when CADGENBENCH_SHA is a fixed commit SHA. Co-authored-by: Cursor <cursoragent@cursor.com>3909559 -
docker: track cadgenbench @main instead of a pinned SHA
Michael Rabinovich +1: CursorPre-v1 the Space should always rebuild against the latest cadgenbench. Default CADGENBENCH_SHA to `main`; lock to a specific commit SHA at the v1 release for reproducible scores (post-gt-swap.md Stage F). Co-authored-by: Cursor <cursoragent@cursor.com>40dd166 -
docker: stop COPYing the removed results.jsonl
Michael RabinovichThe bundled results.jsonl was git rm'd along with the leaderboard's local fallback, but the Dockerfile still listed it in the final COPY, so the build failed with `"/results.jsonl": not found`. Drop it from the COPY; only the *.py sources are needed now.3e18d9a -
leaderboard: drop silent fallback; boot resilient on Hub read failure
Michael RabinovichThe leaderboard previously fell back to a bundled/local results.jsonl on any Hub error, which let an under-scoped Space HF_TOKEN silently serve stale baked-in rows that looked up-to-date. Remove the fallback: the live results.jsonl is the single source of truth and any read failure raises LeaderboardDataError. To stay robust rather than crash the Space at construction time, app.py wraps the readers (_safe_load_split / _safe_load_admin): a failed read yields empty, correctly-shaped tables plus a loud, persistent banner and a gr.Warning toast on refresh / Timer tick, and is logged via logger.exception. No stale or cached data is ever shown in place of a failed read. Also git rm the bundled results.jsonl and add regression tests for the no-fallback contract and the boot-resilience wrappers.a662bfa
-
submit: derive eval-gpu image repo from HF_ORG
Michael Rabinovichthe image lives in the org, so the repo id tracks HF_ORG (matching leaderboard.py) and an org rename stays one space-variable change. on the space HF_ORG is set, so this resolves to the same value as before. the job namespace stays a fixed michaelr27 constant since dispatch is billed to that account. verified resolution for the space, override, and no-HF_ORG cases.c78e980 -
submit: keep eval-gpu image repo as a fixed literal
Michael Rabinovichrevert the env-var indirection; the audit sweep stays prose-only and must not alter dispatch resolution. the repo id and job namespace are real deployed identities, kept as documented constants.bd9b6ba -
submit: derive eval-gpu image repo from HF_ORG
Michael Rabinovich0a04e61 -
leaderboard: log the local-fallback path instead of printing
Michael Rabinovich3dba2e2
-
license: add apache-2.0 header to space modules
Michael Rabinovichb048bc2 -
app: use a colon in the submit-tab system-agnostic note
Michael Rabinovich8a21dae -
readme: describe the space card in client-facing terms
Michael Rabinovich6d31809 -
app: default the admin validation_method to manual
Michael Rabinovichmanual (we re-ran the agent ourselves) is the common case for the maintainer team, so preselect it on the promote radio.46e3a72 -
app: credit Mecado as CAD data source in About tab
Michael Rabinovich +1: CursorCo-authored-by: Cursor <cursoragent@cursor.com>0c44305 -
app: admin tab as a multi-select table with delete
Michael Rabinovichreplaces the single-row dropdown with an editable table: one row per submission, a leading select checkbox the only editable column, the rest read-only context. promote, demote and delete now act on every ticked row at once. promote applies the chosen validation_method to the whole selection. delete sits in a danger-zone accordion behind a confirm checkbox that arms the button, and clears companion artifacts plus the row. a live count line reports how many rows are selected.58ce093 -
admin: bulk promote/demote/delete + admin table loader
Michael Rabinovichadds load_admin_table to the leaderboard reader: a single flat frame of every row (both tiers) with a leading editable select column, for the admin tab to act on. reworks admin.py around bulk helpers (promote_rows, demote_rows, delete_rows) that each do one results.jsonl write for the whole selection; delete also best-effort removes the companion zip and report artifacts. the singular promote_row/demote_row stay as thin wrappers. tests cover the bulk paths, a missing-id abort, empty-selection guards, and artifact deletion, all with the hub mocked.0957a56 -
tests: cover the admin gate and promote/demote helpers
Michael Rabinovichfour unit tests for admin.py: the CADGENBENCH_ADMINS gate (in-set, out-of-set, logged-out, empty set), promote and demote happy paths, and idempotent re-promotion. the results.jsonl read and write that _hub_rmw_results drives are mocked, so the suite makes no hub calls.007a50e -
app: add admin tab for promoting and demoting rows
Michael Rabinovichnew admin tab with a login button, a submission dropdown, a validation_method radio, and mark validated / mark unvalidated buttons. the controls are gated on membership in the CADGENBENCH_ADMINS set via a blocks.load handler, mirroring the submit button's login gate; the promote and demote handlers re-check admin rights server-side and refresh the dropdown plus both leaderboard tables after a write.47c86cf -
admin: promote/demote helpers gated by CADGENBENCH_ADMINS
Michael Rabinovichadds admin.py with is_admin, promote_row and demote_row. promotion sets validation_status to validated and records the evidence type; demotion reverses it and nulls validation_method. row writes reuse submit._hub_rmw_results so the lock and read-modify-write semantics match the submit path. the admin set is read from the CADGENBENCH_ADMINS space variable.0bd3c0a -
submit: drop invisible button-progress bar
Verified signature7655daf -
submit: restore Validating... toast on click
Verified signatureef37489 -
step 10 / jobs-migration: requirements.txt -> dispatch eval to HF Jobs
Verified signature8ff1be8 -
step 10 / jobs-migration: submit.py -> dispatch eval to HF Jobs
Verified signaturefd61157
-
submit: drop eval workers 4 -> 1 (OOM under parallel osmesa)
Michael Rabinovich +1: CursorCo-authored-by: Cursor <cursoragent@cursor.com>ed2a486 -
Dockerfile: swap vtk for vtk-osmesa so off-screen rendering works headless
Michael Rabinovich +1: CursorThe stock pyvista wheel pulls the stock vtk wheel from PyPI, which is built with vtkXOpenGLRenderWindow. That render window class needs an X server even in off_screen mode, so it segfaults the eval worker on the Space's truly-headless container ("DISPLAY=" with no X11 socket). The parent ProcessPoolExecutor sees the segfault as BrokenProcessPool six minutes into the eval, which is exactly the failure mode the first VTK submission hit. vtk-osmesa is the same VTK build but compiled against Mesa OSMesa (pure CPU software rasteriser, statically linked, no display server required). It shares the `import vtk` namespace so pyvista picks it up transparently. This is the canonical PyVista headless-Docker fix (per the PyVista docs + the maintainers' own Docker images). The wheel lives on Kitware's index, not PyPI proper. Co-authored-by: Cursor <cursoragent@cursor.com>545c818 -
submit: dump full eval/report subprocess output to container log on failure
Michael Rabinovich +1: CursorCo-authored-by: Cursor <cursoragent@cursor.com>0648533 -
Dockerfile: bump CADGENBENCH_SHA to b22a53c (ProcessPool spawn fix)
Michael Rabinovich +1: CursorCo-authored-by: Cursor <cursoragent@cursor.com>fa32cdd -
Dockerfile + submit: swap Chromium for VTK; bump eval workers 1 -> 4
Michael Rabinovich +1: CursorPairs with cadgenbench 3fd213d (in-process VTK / PyVista renderer). Dockerfile drops the playwright install + Chromium browser layer (~200 MB), drops PLAYWRIGHT_BROWSERS_PATH, and adds the Mesa GL runtime libs VTK needs for off-screen rendering on the Space's cpu-upgrade tier (libgl1 was already there; libegl1, libegl-mesa0, libglx-mesa0 are new). CADGENBENCH_SHA bumps to 3fd213d. EVAL_WORKER_COUNT goes from "1" back to "4": the VTK renderer is cheap enough that one ProcessPoolExecutor worker per fixture no longer oversubscribes the host the way the old Chromium subprocess did. Co-authored-by: Cursor <cursoragent@cursor.com>0626a6b -
leaderboard: format submitted_at as `YYYY-MM-DD HH:MM UTC`; lock tables read-only
Michael RabinovichTable cells were editable (gradio_leaderboard inherits gr.Dataframe's interactive default); add interactive=False to both Validated and Unvalidated widgets so cell contents aren't accidentally typed over. submitted_at column rendered as raw ISO-8601 ("2026-05-28T07:13:16Z"); apply the same _fmt_timestamp helper the detail panel already uses so the table reads as "2026-05-28 07:13 UTC". Helper moved into leaderboard.py (formatter module) and re-imported by app.py.c4e21b3 -
submit: serialize cadgenbench evaluate to dodge cpu-upgrade contention
Michael RabinovichEVAL_WORKER_COUNT goes from "8" to "1". Removes the debug /debug/render-bench route added in the previous commit. Why: on the Space's cpu-upgrade tier today, a single headless- Chromium render takes ~7s; five in parallel collapse the host and each individual render blows past its 120s subprocess timeout. Same renderer, same fixtures, same Playwright version run on a laptop in ~5s per render with no slowdown under 5-way parallelism. The slow path is HF's shared host, not our renderer. Sequential eval (one fixture at a time, one render at a time) gives each Chromium the whole box. Five fixtures finish in ~30-60s wall time, far under the 15-minute outer EVAL_TIMEOUT_SECONDS budget. Tradeoff: total eval wall time grows linearly with fixture count. For today's five fixtures that's a few seconds slower than the healthy-host parallel case, and a huge cliff better than a failed eval. For the 100-fixture launch this becomes ~10 minutes per submission, which is the limit of this approach: a r…6e3ab50 -
debug: add /debug/render-bench route for one-shot Chromium timing
Michael RabinovichRead-only FastAPI endpoint mounted on the Space's parent app. Sequentially times `render_step` against each fixture's input.step in cadgenbench-data and returns per-fixture seconds + view count. Used to compare per-render cost on the Space's container vs a local reference when eval has been timing out at the 120s render_step ceiling but nothing on the render path changed in our code. Curl-fetchable under Bearer auth (the Space is private): curl -H "Authorization: Bearer $HF_TOKEN" \ https://<space>.hf.space/debug/render-bench Not user-visible (no UI), no side effects, no row writes. Safe to remove once the diagnosis lands.37e45d8 -
submit+app: gate Submit on HF OAuth; populate hf_username from profile
Michael RabinovichBundle 1+2 C10. Login-required submit shaped per the bundle spec. submit.py: - `handle_submit` grows a `profile: gr.OAuthProfile | None` arg. Gradio dependency-injects the visitor's profile based on the signature. None -> immediate `gr.Error("Please log in via the HF button before submitting.")` so a UI mishap can't write an anonymous row. - `_build_pending_row` grows an `hf_username=None` kwarg. `handle_submit` passes `profile.username` through so the row carries the canonical HF identity (not a free-text claim in meta.json's submitter_name). app.py: - New `gr.LoginButton()` in the Submit tab above the zip dropzone. - Submit button starts `interactive=False`; new `_enable_submit_when_logged_in(profile)` reads the visitor's OAuth profile via `blocks.load` and flips the button on / off per page load. Server-side gate in `handle_submit` mirrors this so a logged-out user who somehow bypasses the disabled-button UI still gets rejected. requirements.txt: - `gradio…c87b253 -
submit+app: toast-based feedback (validating / queued / refreshed)
Michael RabinovichBundle 1+2 C9. Replaces the static markdown reply on the Submit tab with gr.Info / gr.Error toasts, and adds a refresh toast on the manual Refresh button (the Timer auto-refresh stays silent - a toast every 10s would be noise). submit.py: - handle_submit no longer returns a markdown string. It side- effects: gr.Info("Validating submission...") at the top of the validation stage, gr.Info("Submission <id> queued ...") after the row + zip are on the Hub and the worker has been spawned. Every rejection path raises gr.Error so the user gets a red toast and the handler aborts; the outer try/finally still runs cleanup. Internal _ValidationError + _HubWriteError sentinels preserved. - _validate_form returns a plain message (no `**Error:**` markdown wrap; the caller wraps into gr.Error). - All four rejection paths covered: 1. Form (no zip attached). 2. Validation gate (extract / meta / fixtures / steps). 3. Dedup gate (sha256 match on existing row). 4. Hub write…6facf47
-
leaderboard+app: combined CSV download with validation_status discriminator
Michael RabinovichBundle 1+2 C8. One file, both tiers, downloaded via `gr.DownloadButton` next to the existing Refresh button. leaderboard.py: - New `CSV_COLUMNS` constant (wider than the on-screen table: raw aggregate_score / validity_rate, plus provenance fields like submission_sha256 + cadgenbench_data_revision + notes + failure_reason). `validation_status` is the discriminator that lets a reader grep the file for one tier or the other. - New `build_combined_csv()` reads the same rows the leaderboard reader does, applies the same status / validation_status defaults, projects to CSV_COLUMNS, sort: validated rows on top (highest score first) then unvalidated, then writes to a unique /tmp file and returns the path. Fresh on every click so the export reflects the latest data, not a snapshot captured at boot. app.py: - Imports `build_combined_csv`. The Refresh button and the new `gr.DownloadButton("Download CSV", size="sm")` live in a single `gr.Row` so they sit side-by-side unde…f585077 -
app: Citation + Validation Guidelines accordions, validation link in About
Michael RabinovichBundle 1+2 C7. Two collapsed accordions above the leaderboard tables plus a validation-policy bullet on the About tab. - Validation Guidelines accordion (collapsed by default, sits above the tables). Carries the two-tier story in two lines (unvalidated -> validated, four evidence types) plus a link to the canonical policy doc in the main repo at `cadgenbench/docs/benchmark/validation.md`. Bundle 4 creates the file; the link will 404 until that lands, then resolves. - Citation accordion (collapsed by default, below Validation Guidelines). Carries the verbatim BibTeX entry locked in space-setup/bundles/1-2-space-ux.md (cite key cadgenbench2026, Space URL as howpublished). Rendered in a `gr.Code` with language=None (no BibTeX highlighter ships with gradio) and show_line_numbers off so the entry stays copy-paste tidy. - About tab picks up a new bullet linking the same validation doc. VALIDATION_DOC_URL is centralised at module level so the Validation Guidelines markdo…97b9a4a -
app: render reports inline via iframe srcdoc (private-Space safe)
Michael RabinovichPolish follow-up on C6. Reports now render inside the Space, inside the same iframe HF uses to host the leaderboard, with no second HTTP request leaving the browser. Why: the Space is private, and HF's edge gates same-origin pathname navigations on a short-lived __sign JWT that browsers don't carry forward when a user clicks a relative link. The previous design (submission_name -> /reports/<id>.html) worked server-side under Bearer auth (verified) but 404'd in the browser. Confirmed via external review and reproduced with a requests.Session cookie test. This commit: - Drops the submission_name -> /reports/<id>.html link wrapping from the rendered table; the cell stays plain text. The underlying `report_url` column (relative URL to the proxy) is still computed for the modern-pipeline gate but no longer surfaced as a clickable cell. - Adds a `gr.HTML` viewer below the detail panel. Row-click triggers `_format_detail_and_report`, which returns (detail_markdown, report_ifram…0e3b21f -
app+leaderboard: link submission_name to a Space-side report proxy
Michael RabinovichPolish follow-up #2 on C6. Three user-visible changes plus the plumbing required to make them work. 1. Drop the `report` and `zip` columns from the rendered table. `submission_name` becomes the report's deep link (typical HF leaderboard pattern: the system / model name doubles as the link target; cf. Open LLM Leaderboard, DABstep, BigCodeBench). Modern completed rows get `[<name>](/reports/<sid>.html)`; pending, failed, and legacy seed rows fall back to plain text so there's no broken click. The hidden `submission_blob_url` and the computed `report_url` ride along in the DataFrame and surface in the row-click detail panel only. 2. Rename the agent-URL column from `agent` to `model details (optional)`. Missing cells render as italic `_None_` so the optionality is explicit instead of a blank. "Agent" was confusing; the schema lets `agent_url` be code OR paper OR a blog post and "agent" reads narrow. 3. Add a Space-side proxy for the per-submission H…77edebf -
app+leaderboard: detail panel polish (rename link columns, fix broken report links)
Michael RabinovichPolish follow-up on C6, three fixes after the first detail-panel commit landed: 1. Rename display columns from snake_case schema names to clean short headers. `agent_url` -> `agent`, `submission_blob_url` -> `zip`, `report_url` -> `report`. The rename happens at projection time via `df.rename`, so the schema in results.jsonl is unchanged. Headers now read as plain English ("agent", "zip", "report") rather than a python dict. 2. The `agent` column shows the URL itself (scheme stripped, truncated past 40 chars) as link text instead of a fixed "code" label. The schema says agent_url can be code OR paper; the shortened URL is the only honest hint about what's behind the click. Missing values render as italic `_None_` rather than a blank cell, so the optionality is explicit. 3. The `report` link is now gated on the row's `submission_sha256` being non-null in addition to status == "completed". Legacy seed rows pre-date the modern submit pipeline (which …1a8f331 -
app: row-click detail panel below the leaderboard tables
Michael RabinovichBundle 1+2 C6. Single shared `gr.Markdown` panel below both Leaderboard widgets; either table's `.select(...)` event populates it with the clicked row's notes, agent_url, submitted_at, submission zip + report links (always), and failure_reason (failed rows only). Spec'd shape: one panel, two select sources. leaderboard.py: - LEADERBOARD_COLS + VALIDATED_LEADERBOARD_COLS each grow by three hidden detail-only fields (submission_id, notes, failure_reason). They ride in the underlying DataFrame so the select handler can read them out directly without a parallel state cache. - New LEADERBOARD_HIDE_COLUMNS constant lists the columns hidden from view; both Leaderboard widgets in app.py consume it. - `_project_and_format` now fills missing columns with None (legacy rows can lack optional fields like notes; the panel needs the column-set to be stable regardless of source-row shape). app.py: - pandas imported (for the helper's pd.isna check). - New `_format_detail(df, evt)` helper …3112173 -
leaderboard: markdown link columns for agent_url, submission, report
Michael RabinovichBundle 1+2 C5. Three new columns on both tables (validated + unvalidated) carry clickable links rendered via gradio_leaderboard's `datatype="markdown"`: - agent_url: link to the agent's source / paper if the row's meta.json provided one (label "code"; empty cell when null). - submission_blob_url: link to the uploaded zip on the submissions dataset (label "zip"; populated on every non-failed row, the same URL the submit handler computed at upload time). - report_url: built at read time from `submission_id` plus HF_SUBMISSIONS_REPO; points at `reports/<id>.html` under /resolve/main/ so the browser renders the report directly. Only emitted for `status == "completed"` rows. leaderboard.py: - LEADERBOARD_COLS + VALIDATED_LEADERBOARD_COLS grow by three. - New LEADERBOARD_DATATYPES + VALIDATED_LEADERBOARD_DATATYPES constants mark the link columns as "markdown", everything else as "str". Tied to the column lists by a single helper so the two stay in lockstep. - New _agent_u…53de73a -
submit: default validation + hf_username fields on the pending row
Michael RabinovichBundle 1+2 C4. `_build_pending_row` adds three new keys matching the schema bump from C1, in the schema.md column order: - validation_status = "unvalidated" (every new row starts in the unvalidated tier; maintainers promote via the dataset repo after methodology review). - validation_method = None (set by the maintainer at promotion time to one of code | traces | api | manual). - hf_username = None (placeholder; C10 plumbs through the value from gradio.OAuthProfile.username once the LoginButton lands). Pre-existing fields, submitted_at stamping, the cadgenbench version + data revision tags, the score-shaped null fields, the blob URL, and the sha256 are untouched. Dedup gate still pivots on submission_sha256 exactly as before. tests/test_submit.py covers the C4 acceptance: - new-row defaults: validation_status is "unvalidated", validation_method + hf_username are None; - sha256 path passthrough: the hash argument lands on the row's submission_sha256 key intact (existing …a533dd2 -
docs: add leaderboard discovery tags to README front-matter
Michael Rabinovichtags: surfaces this Space in the Hub's leaderboard discovery filter (huggingface.co/spaces?other=leaderboard) and faceted searches by modality, submission type, and eval type. The set blends the empirical vocabulary across the top 50 leaderboard-tagged Spaces with three truthful novel descriptors: modality:3d (closest existing values are modality:text / tools, neither accurate for STEP geometry), eval:cad (paired with eval:generation so the standard "generation" chip also surfaces us), and judge:auto (eval is metric-based, no LLM/human judge).36546fc -
app: split Leaderboard into Validated + Unvalidated tables
Michael RabinovichBundle 1+2 C3. Swaps the single Leaderboard widget for two stacked ones (Validated on top, Unvalidated below) and wires the existing auto-refresh Timer + Refresh button to push fresh dataframes into both. Initial render reads via the C2 `load_leaderboard_split()`; existing rows all land in Unvalidated until a maintainer flips `validation_status` to `"validated"` on a row in the submissions dataset. - app.py: replaces `df_view` with `validated_view` + `unvalidated_view`. Single timer tick now fans out to both widgets (load split runs once per tick, both halves get pushed). Refresh button same fan-out shape. Inline comment points at the validation policy decision doc. - leaderboard.py: drops the now-unused single-DataFrame `load_leaderboard()` per the C3 cleanup half. Stale docstring references that pointed at the dead function got smoothed out (the split reader's doc is self-contained now). - tests/test_smoke.py: picks up the C3 acceptance with a new `test_two_leaderboa…046548a -
license: add Apache-2.0
Michael Rabinovich8676e14 -
leaderboard: add load_leaderboard_split() for two-tier viewer
Michael RabinovichBundle 1+2 C2. Additive new function for the C3 two-stacked-table swap; existing single-DataFrame load_leaderboard() stays put until C3 flips the caller in app.py + drops it. Keeps the live Space green between commits. - VALIDATED_LEADERBOARD_COLS: same shape as LEADERBOARD_COLS plus `validation_method` (always null on unvalidated rows, so omitted from that tier rather than rendered). - load_leaderboard_split() -> (validated_df, unvalidated_df): reads results.jsonl the same way as load_leaderboard(), splits rows on `validation_status` (defensive: anything not literally "validated" lands in unvalidated, which covers legacy + null + any future-unknown value), runs the same status-aware cell formatting per tier via the new _project_and_format helper. - _project_and_format: shared sort + projection + pending/failed cell tagging so the two tiers produce identically-shaped cells. Legacy default: rows without `validation_status` get "unvalidated" via row-level fill. No resul…0dd7215 -
docs: link companion datasets in README front-matter
Michael Rabinovichdatasets: lists the two companion dataset repos so the Hub renders them as cards on the Space page (and so the link is explicit rather than relying on auto-parse from hardcoded repo IDs in app.py / leaderboard.py). - HuggingAI4Engineering/cadgenbench-submissions: the results + per-submission report dataset the leaderboard reads from. - HuggingAI4Engineering/cadgenbench-data: the public fixture inputs. cadgenbench-data-gt is omitted on purpose (permanently private).1402833 -
docs: add hf_oauth + apache-2.0 license to README front-matter
Michael Rabinovichhf_oauth: true is what wires the LoginButton + OAuth callback the Submit tab relies on; without it the auth-required submit flow is silently broken (login click no-ops, no token reaches the handler). license: apache-2.0 matches the HF Leaderboards template (gradio-templates/leaderboard) and the rest of the cadgenbench code surface, and renders a license badge on the Space page.0bb6044 -
tests: pytest + playwright headless smoke harness
Michael RabinovichFirst Space-side test harness; sets up the floor for the Phase D bundle's per-commit unit + UI tests (split leaderboard, pending-row defaults, OAuth-required submit) without slipping a regression past a build-only check. requirements-dev.txt pulls -r requirements.txt then adds pytest + playwright. One `pip install -r requirements-dev.txt && playwright install chromium` brings a fresh venv up to speed. tests/conftest.py exposes a session-scoped `app_url` fixture: boots app.py in a subprocess on a free port, polls until the HTTP server answers, yields the URL, tears the process down on teardown. Logs land in a tmp file so a startup failure surfaces the real Gradio traceback rather than a bare RuntimeError. Boot-time stuck-pending sweep in submit.py is disabled via CADGENBENCH_DISABLE_BOOT_SWEEP=1 so the fixture is fast and offline-tolerant. tests/test_smoke.py launches headless Chromium via playwright's sync API, navigates to the fixture's URL, and asserts the Leaderboard / Submit / A…21b071a