Commit History
-
Serve rotating WebP turntables + GT generator
Michael Rabinovich +1: CursorPoint the gallery and proxy routes at rotating.webp (submissions and GT), publish the full per-fixture renders folder, and add a reusable one-time GT turntable generator that renders from the cached GT mesh. Co-authored-by: Cursor <cursoragent@cursor.com>c1cb5e4 -
stage shards via bucket API instead of volume mount
Michael RabinovichAvoids Space volume-mount issues on an existing Space: shard jobs sync artifacts to the HF bucket via the bucket API, and the Space syncs them back down to merge. Gated on CADGENBENCH_SHARD_BUCKET.920b1b4 -
fix HF Jobs polling namespace
Michael Rabinovichdd284f3 -
leaderboard: open reports in new tab
Michael RabinovichNow that the Space is public, link completed submissions directly to their proxied HTML reports instead of inlining large report payloads through the row-click detail panel.5fb3ebc -
stage shard artifacts through HF bucket
Michael Rabinovich6ffd043 -
test: fix leaderboard smoke test for gallery-default tab + sr-only label
Michael Rabinovich +1: CursorGallery is now the default tab, so the Leaderboard content is hidden on load; click the Leaderboard tab before asserting. Scope the label locator to the visible <p> since the Leaderboard widget also renders a screen-reader-only <caption> with the same text (strict-mode 2-element violation otherwise). Co-authored-by: Cursor <cursoragent@cursor.com>1004adf -
gallery: searchable fixture picker, URL-persisted picks, cached render proxies
Michael Rabinovich +1: CursorReplace the always-open fixture chip grid with the compact reference UI: current picks as removable pills + an "Add fixture" button opening a searchable, grouped (Generation/Editing) dropdown, capped at 3 with a "remove one to add another" note. Persist the 3 picks in ?fixtures= so shared links reproduce the view; URL read/replaceState are try/catch guarded for sandboxed iframes. Drop the external Google Fonts @import for system font stacks so the page never errors in a sandboxed frame. Serve thumbnails as lazy-loaded, hard-cached /render and /gt-render proxy URLs (loading="lazy" + decoding="async" + CSS-constrained width) instead of base64-inlining every tile, so only the ~33 visible thumbnails load and fixture swaps / repeat visits hit cache. Requires the Space to be public. Fix a stale test expecting the old 6-tuple _admin_delete signature. Co-authored-by: Cursor <cursoragent@cursor.com>4a9408a -
admin: stop pending jobs before delete
Michael RabinovichAdd an admin action that cancels matching in-flight HF Jobs before deleting submissions, and keep the admin table refreshed so pending rows stay visible.c4b5d70 -
submit: stream live eval progress to the submitter's status panel
Michael Rabinovich +1: CursorAdds a small in-process progress registry (progress.py) that the eval worker publishes stage notes to (queued -> waiting for GPU -> evaluating -> collecting results -> done/failed, plus an "X of N chunks done" count for sharded runs). handle_submit keeps its generator alive after queuing and streams those notes into the personal status panel until a terminal stage or a backstop deadline. The shared leaderboard table stays coarse; this is the personal-view feedback only, so it adds no write load to the results file. Also fixes the _with_hub_retries tests for the newer huggingface_hub HfHubHTTPError(response=...) signature. Co-authored-by: Cursor <cursoragent@cursor.com>b0f4559 -
submit: publish merged report + gallery in one commit
Michael Rabinovich +1: CursorReplace the per-file gallery-thumbnail uploads (one upload_file commit per fixture, ~80+ for a full fan-out) with a single create_commit that stages reports/<id>.{html,json} plus every renders/<id>/<fixture>.png at once. Collapses finalize from ~86 commits to one publish commit, cutting post-shard finalize from minutes to well under a minute and removing the 429 "concurrency queue" failures that stranded earlier submissions. Co-authored-by: Cursor <cursoragent@cursor.com>07430f9 -
submit: retry Hub commits on 429/5xx + persistent status panel
Michael Rabinovich +1: CursorWrap every Space-side commit (zip upload, results.jsonl RMW, report + gallery uploads, shard cleanup) in a backoff-with-jitter retry that honors Retry-After, so a rate-limited submissions repo delays rather than fails a submit. Make handle_submit a generator streaming a durable status panel (validating -> uploading -> queued, or the rejection reason) instead of relying on transient toasts. Co-authored-by: Cursor <cursoragent@cursor.com>b224eee
-
submit: allow missing fixture outputs
Michael Rabinovichdc8ff2a -
gallery: refresh after admin changes
Michael Rabinovichdaae24c -
pin cadgenbench to e446cca (benchmark-v1 aligner + sidecar meshes)
Michael Rabinovich +1: CursorCo-authored-by: Cursor <cursoragent@cursor.com>30909aa -
admin: refresh table on auth load
Michael Rabinovich02751ff
-
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