Review: PhET Interactive Simulations mirror — real catalogue, responsive repair and task verifiers (site by @ZhongyangLi0124) - #114
Conversation
Blind review — frozen runs, WHR-029 (phet_simulations)Independent pass on the frozen bundle Result: What produced the runs
Commits / assets
Coverage
FailuresNone. Checks NOT executed / evidence limits
|
Independent blind review — bundle
|
Adds Flask app, templates, and seed DB for PhET Interactive Simulations (https://phet.colorado.edu/), claiming port slot 40015. Catalog: 98 simulations across 5 subjects (physics, chemistry, math, biology, earth-science), 4 grade levels (elementary, middle, high, university), and 28 languages (incl. 3 RTL scripts). Every primary filter bucket clears the >=20-record threshold. Models: User, Subject, GradeLevel, Language, Simulation, Activity, SavedSimulation. Routes: 17 public + 2 JSON APIs + /_health, all reachable from /. Auth via Flask-Login + bcrypt; saves via CSRF-protected JSON endpoints. Idempotency: every seed_* helper early-returns when its table is populated; verified byte-identical (md5 e094a2ee...) across the control_server reset cycle (rm -rf instance; cp -a instance_seed instance; re-import app). Tested under the exact Dockerfile pin set (Flask 3.1.0, SQLAlchemy 2.0.36, Werkzeug 3.1.3). Per the port-slot convention, also: - websyn_start.sh: append to SITES, bump 15 -> 16 in startup messages - control_server.py: append 'phet_simulations' to SITES list - Dockerfile: EXPOSE 40000-40014 -> 40000-40015 Seed DB (instance_seed/phet_simulations.db, 143KB) packs into a 17KB phet_simulations.tar.gz via scripts/extract_assets.sh and ships separately via the Hugging Face dataset. .assets-revision will need a bump after the HF PR merges. tasks.jsonl: 43 benchmark prompts covering catalog browse, subject filters, simulation detail extraction, search, translations, teacher activities, and the account save flow.
Replace placeholder UI with a high-fidelity mirror of the real
PhET Interactive Simulations site.
Visual:
- Real PhET yellow trademark logo + CU Boulder logo in white header bar
- Right-aligned nav (Simulations / Studio / Teaching / Research / Initiatives)
with hover dropdowns; collapsing search icon + profile icon
- Dismissible pink educator banner
- Photo-backed top hero ("Interactive Simulations for Science and Math")
- 5 pastel subject squares in PhET's canonical order (Physics, Math &
Statistics, Chemistry, Earth & Space, Biology) with their SVG icons
- Photo-backed Teaching Resources callout
- "Interact. Discover. Learn." stats section with real iconography
- Secondary STEM hero photo + centered "Supported in part by" + Donate CTA
- Real footer: social row, 4-column links, language selector, app-store
badges, sponsor logos strip (Moore, Hewlett, NSF, Mastercard, Yidan)
Simulations browse page:
- Underwater illustration banner + Browse / Filter / Customize sub-tabs
- Filter view is the default; left sidebar with collapsible subject tree,
grade level, compatibility, release type, inclusive features, locale,
apply / clear, active-filter chips, sort dropdown, results count
- Browse view groups sims by subject with "View all »" links
Simulation detail page:
- Large screenshot with hover "Play" overlay
- Tabbed sections (About / Teaching Resources / Activities /
Translations / Credits) plus topics, learning goals, related sims
- "Supported by" sidebar, share row, pink Explore More + Donate CTA
Backend wiring:
- Index route adds total_activities for the stats section
- Simulations route exposes view tab and per-subject buckets
- App.py exposes available_thumbnails so cards fall back gracefully
when a slug has no screenshot
Assets ship via the paired HF dataset tarball (not committed):
- 98 sim screenshots (49 from PhET CDN, 49 generated placeholders)
- PhET + CU Boulder logos, subject icons, sponsor logos, hero photos,
app-store badges, underwater simulations banner
Seed DB unchanged, /reset/phet_simulations remains byte-identical
(md5 e094a2ee23369d3b60232f49f4ac691c).
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Addresses all points from MufanQiu's review:
1. Homepage now renders simulation grids (BLOCKER). index.html displays
the route's featured / new_sims / most_played context in three
subject-section rows (20 sim cards), so task --41 is solvable from /.
2. 'New' release filter is functional (MAJOR). simulations() now reads
release=new (is_new) and release=updated (released >= 2024-01-01);
the sidebar checkboxes persist state and the active-filter chip,
sort, and pagination links carry the param. Task --21 is solvable
via UI (answer: 7 New sims, all released 2025).
3. No DB mutation on GET (MAJOR). Removed play_count increment in
simulation_detail and download_count increment in activity_detail;
counts are now fixed seed data, so tasks --6/--31/--41 have
deterministic answers.
4. Seed/task disambiguation (MINOR):
- Activities carry explicit pairwise-distinct download counts;
unique most-downloaded = Net Force Investigation (8742), paired
with Forces and Motion: Basics (task --12).
- Sim versions derived deterministically from seed constants
(47 distinct versions; Wave Interference = 1.5.2, task --28).
- Tasks --6, --24, --28, --31 reworded with tighter constraints so
each has exactly one valid answer (verified programmatically).
Seed DB rebuilt; reset cycle remains byte-idempotent
(md5 48ca438b8ac6dab37a6503a4e5574503). 66/66 routes return 200.
New phet_simulations.tar.gz must be re-uploaded to the HF dataset and
.assets-revision repinned to the HF merge SHA (separate commit).
…40024 Rebased @ZhongyangLi0124's contribution onto current main. The PR's original 15/16-site registry edits were superseded, so phet_simulations is appended to websyn_start.sh and control_server.py at index 24 and Dockerfile exposes 40000-40024. Also sweeps the repo docs (README, AGENTS, CONTRIBUTING, CLAUDE, agent_demo) from 24 to 25 sites, points tasks.jsonl at port 40024, and updates the rotten_tomatoes registry assertions that pin the SITES tail and EXPOSE range. The port slot stays provisional until merge. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Every page rendered an 837px-wide document at both 768px and 390px viewports because the desktop nav, search form and user menu never collapsed. Body content stayed in the left 390px and the rest of each page was blank gutter. - collapse the primary nav behind a toggle below 900px, with sub-links shown inline inside the open panel so they stay reachable without hover; - inline the search field, which was absolutely positioned off-screen in a narrow bar, and drop the CU mark below 520px; - reflow the pastel subject grid at <=1000px, which previously kept five columns until 760px and overhung a 768px viewport; - let grid children shrink (min-width:0) and cap image width; - contain the simulation detail tab strip's overflow inside itself. Verified across 20 pages x 1440/768/390: 60 of 60 captures now have scrollWidth equal to the viewport, down from 40 overflowing. All 20 desktop pages keep byte-identical layout heights, so the change is additive below 900px only. Reset still restores the seed byte-identically. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
43 of the 98 seeded simulations did not exist on phet.colorado.edu. They were invented, with generated titles, descriptions, subjects, grade bands, versions, release dates, play counts and language lists, and each was illustrated by a purple gradient placeholder card rather than a screenshot. Six more existed upstream but under a mangled slug. The catalogue is now the real HTML5 library, harvested 2026-09-13: - 120 simulations from the official metadata service, with upstream titles, descriptions and sample learning goals; - subject and topic membership from the numeric facet ids, whose meaning was confirmed by intersecting each id against the live filter (Physics 65, Math & Statistics 51, Chemistry 35, Earth & Space 19, Biology 8 -- exact matches on all four); - grade bands, version strings, release and last-updated dates, the per-sim locale list and relatedSimulations, all from the same feed; - the topic strip shown on each upstream simulation page; - the 131-language translation table from the upstream translated-sims page, whose HTML5 counts agree with the seeded per-locale counts; - official 600px screenshots for all 120 simulations, replacing every placeholder. Facts PhET does not publish are removed rather than invented: play counts, download counts and per-simulation runtime are gone from the model, the homepage's third rail is now Recently Updated, and catalogue sort offers Recently updated and Most translated instead of Most played. Also fixed in the same pass: - the topic facet now filters (it read no parameter before, leaving 11 topic, 3 compatibility and 3 inclusive-feature checkboxes inert); the compatibility and inclusive groups, which this HTML5-only snapshot cannot back with data, are replaced by a statement of that fact; - the Customize tab rendered an empty page because it fell through to the browse branch with no data, and now lists the 49 simulations upstream marks as available in PhET Studio; - search tokenises, so "build atom" reaches Build an Atom instead of returning nothing; - the filter sidebar gains Heat & Thermo and renames Numbers and Quantum Mathematics to the upstream Math Concepts and Math Applications; - teacher activities are relabelled as benchmark fixtures with synthetic authors and now point at simulations that exist, instead of attributing invented lesson plans to real, named PhET contributors. Reset stays byte-identical and re-seeding on restart is a no-op. Tasks that were anchored on fabricated simulations still need re-anchoring; that is the next commit. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…ract The 43 contributor tasks could not survive the catalogue rebuild: nine of them targeted simulations that do not exist upstream, four more read a play counter that PhET does not publish and that is now gone, one asked which scripts are right-to-left (answerable without opening the site) and one only asked the agent to click through to a language page, leaving nothing to grade. This replaces them with 18 tasks anchored on facts that live on this site and that a model cannot recall: exact version strings, exact release dates, exact per-language and per-facet counts, the related-simulations list, account state. None of those appear on a listing card, so each one requires opening the page that carries it. Adds the reviewer grading contract: verify_lib.py plus verify_0..verify_17, recorded as verifier_path and a rules-only judge_rubric on every row. Ground truth is hardcoded in the verifiers; tasks.jsonl has no answer key. Validation, all deterministic (--no_llm True): - 18 of 18 scripted UI walks pass their verifier; - an 88-cell adversarial matrix matches every expectation - no-op, wrong answer, shortcut without navigation, state mismatch on the two stateful tasks, and an unexpected database write on each read-only task all fail on the intended check; - 16 legitimate rephrasings and alternate routes all still pass. Two verifier defects surfaced during that validation and are fixed here: task 2 rejected a correct answer that echoed the question's own phrase "not offered at university level", and the shared number matcher accepted only digits, so an answer of "Five" was wrongly failed. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Two defects surfaced while reconciling the independent review. The simulation detail page's save form had no note field, yet the API stores a note and a task asks for one. The task was therefore not completable through the UI at all; only a direct API call could satisfy it. Add the field, send it from main.js, and render any existing note back into it. The translations grid applied direction:rtl to the whole card for right-to-left languages, which reversed the English count line as well: Arabic rendered "simulations 119" instead of "119 simulations". Scope the direction to the native-name element and isolate the English name and count. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The adversarial matrix found three verifiers that a wrong answer could satisfy. verify_13 and verify_14 graded the saved-simulation tasks purely on the database delta, so an answer describing an unrelated simulation still passed once the save itself had happened. Both now require the answer to name the simulation it saved. has_number accepted a digit appearing anywhere in the answer, so "version 9.9.9" satisfied a check for the count 9. Add verify_lib.counts(), which requires the number to be reported as a count of the thing being counted, and use it for the four small-count tasks. It still accepts the natural phrasings: "9 simulations", "nine simulations", "Simulations: 9", "there are 9". Ship the matrix itself under verify/tests/ so the result is reproducible. It is excluded from the image by .dockerignore. 18/18 canonical runs pass, the 90-cell matrix matches every expectation, and six legitimate rephrasings still pass. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Upstream merged fedex, webmd_doctor, healthline and kaggle while this branch was in review, taking the registry to 28 sites and 40024 with it. Rebase onto current main and take the next free slot instead. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
1d59a39 to
629f56c
Compare
Points .assets-revision at ChilleD/WebHarbor discussions/79 so this branch fetches and builds without waiting for that PR to merge. The commit is upstream's current pin plus phet_simulations.tar.gz; every other archive is byte-identical, verified by comparing the file OIDs of both trees. Temporary. Repin to the merge commit once aiming-lab#79 lands. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Verification update — full environment now covered, branch builds standaloneTwo gaps from the original description are closed. Full 29-site environment smoke: greenRun 34804778961, artifact
The run takes under four minutes, which looks fast for a 29-site image. The artifact is what It ran on a fork branch ( The branch no longer waits on the asset PR
That commit is upstream's current pin It is a temporary pin and wants moving to the merge commit once #79 lands — happy to push that, Still open, and not mine to fix
Head is now |
This is the review vehicle for #29. It keeps @ZhongyangLi0124's three commits intact
(authorship preserved, rebased onto current
main) and adds the reviewer's repairs andgrading contract. Registered as site 29 on port 40028.
What the review found
The Flask app, auth, CSRF and reset behaviour were sound. Two things were not.
43 of the 98 seeded simulations do not exist on phet.colorado.edu. Each of the 49
placeholder thumbnails the original PR describes is a purple gradient card. Checking their
slugs upstream: 2 exist as written, 4 exist under a de-mangled slug (
beer-s-law-lab→beers-law-lab, same for Coulomb's, Faraday's and Hooke's Law), and 43 return 404 —Cellular Respiration, Photosynthesis, Water Cycle, Predator-Prey Dynamics, Mendelian Genetics,
Punnett Squares and 37 others, invented along with their titles, descriptions, subjects, grade
bands, versions, release dates, play counts and language lists. Nine contributor tasks were
anchored on them. This could not be fixed by swapping images: there is no screenshot for a
simulation that does not exist.
Every page overflowed horizontally below ~840px. The desktop nav, search form and user menu
never collapsed and by themselves forced the document to 837px at both 768 and 390, leaving body
content in the left 390px with the rest of each page blank.
What this PR does
1877093,e0c1a9d,4e6cd90c7b5a41aa2b5120c6eec8ea5e87c25fa7a9908ef82629f56cThe catalogue is the real HTML5 library, harvested 2026-09-13 from PhET's official metadata
service: 120 simulations with upstream titles, descriptions and sample learning goals; subject and
topic membership from the numeric facet ids, whose meaning was confirmed by intersecting each id
against the live filter (Physics 65, Math & Statistics 51, Chemistry 35, Earth & Space 19,
Biology 8 — exact on all four); real grade bands, version strings, release and last-updated dates,
per-simulation locale lists and
relatedSimulations; the topic strip from each upstreamsimulation page; the 131-language translation table, whose upstream HTML5 counts agree with the
seeded per-locale counts; and the official 600px screenshot for all 120, replacing every
placeholder. PhET simulations are CC-BY.
Facts PhET does not publish are removed rather than invented. Play counts, download counts and
per-simulation runtime are gone from the model. The homepage's third rail is Recently Updated and
the catalogue sorts by Recently updated / Most translated.
Also fixed: the topic facet now filters (the route read no such parameter, leaving 11 topic, 3
compatibility and 3 inclusive-feature checkboxes inert — the latter two groups, which an
HTML5-only snapshot cannot back with data, are replaced by a statement of that fact); the
Customize tab rendered an empty page and now lists the 49 simulations upstream marks as available
in PhET Studio; search tokenises, so
build atomreaches Build an Atom instead of returningnothing; the sidebar gains Heat & Thermo and uses upstream's Math Concepts / Math Applications
names; teacher activities are relabelled as benchmark fixtures with synthetic authors rather than
attributing invented lesson plans to real, named PhET contributors; the save form gained the note
field its API always accepted; and right-to-left language cards no longer reverse their English
text (
simulations 119→119 simulations).Grading contract
18 tasks replace the contributor's 43. Nine targeted fabricated simulations, four read the removed
play counter, one asked which scripts are right-to-left (answerable without opening the site) and
one only asked the agent to click through to a page with nothing to report. The new set is
anchored on facts that live on this site and that a model cannot recall — exact version strings,
release dates, per-language and per-facet counts, the related-simulations list, account state —
and none appears on a listing card, so each requires opening the page that carries it.
sites/phet_simulations/verify/verify_lib.pyplusverify_0..verify_17, recorded asverifier_pathand a rules-onlyjudge_rubricon every row. Ground truth is hardcoded in theverifiers;
tasks.jsonlhas no answer key.Validation, all deterministic (
--no_llm True):initial.dbandafter.dbrather than reading live state;navigation, an unexpected write on every read-only task, and a missing state change on both
stateful tasks all fail on the intended check. The matrix ships at
sites/phet_simulations/verify/tests/adversarial_matrix.py;Three verifier defects surfaced in that matrix and are fixed: the two saved-simulation tasks
graded purely on the database delta, so an answer describing an unrelated simulation passed; and
a bare number check accepted
version 9.9.9as the count 9. Counts are now bound to the thingthey count.
Independent review
The frozen runs were judged in a separate session with no access to the verifiers, their verdicts,
or any reviewer conclusion. Result: 18 PASS / 0 FAIL, all 226 bundle files hash-verified, all
18
initial.dbbyte-identical (clean per-task reset), both stateful tasks confirmed bybefore/after diff. It agrees with the deterministic run 18/18.
An earlier round on a previous bundle also returned 18/18 but flagged evidence-quality gaps.
Acting on those exposed the save-note defect above (the run harness had been issuing a raw API
call and recording it as a button click, which hid that the task was not completable through the
UI at all), the RTL defect, and batch-level state snapshots presented as per-task. All three are
fixed and the runs were re-recorded with a reset and a state snapshot around every task.
Assets
Paired asset PR: ChilleD/WebHarbor discussions/79, superseding #19 whose seed carries the
fabricated catalogue and the placeholder thumbnails.
A site built from this branch's tracked code plus that tarball alone boots and serves every route,
thumbnail, logo and hero image.
.assets-revisionis pinned to that PR commit (82c2764b…) so this branch builds standalonerather than waiting on the merge. That commit is upstream's current pin (
64264d0) plusphet_simulations.tar.gz; every other archive is byte-identical, verified by comparing the fileOIDs of both trees. Repin to the merge commit once #79 lands.
Verification run
POST /reset/phet_simulationsPOST /reset-allEXPOSE 8101 40000-40028Full 29-site environment smoke, run 34804778961 (green, artifact attached to the run):
./scripts/build.shcompletesok=true, all alivereset-allreset-allThe workflow lives on a separate
ci/phet-full-smokebranch and is not part of this PR. Its treediffers from this PR's head only by that workflow file.
Known limitations
The full 29-site image build was not run.Done — see the verification table above.trajectory records that in
run_kind.sites/rotten_tomatoes/tests/test_environment_quality.py::test_task_manifest_and_registryfails on pristine
mainat line 101 — it asserts a literal site list that upstream reflowedacross lines. Reproduced in a clean worktree of
origin/main; left untouched here.byte-reproducible from source alone. The shipped tarball is the baseline; reset stays
byte-identical.
Recently Updated so simulations remain reachable from the homepage for the benchmark — a
deliberate deviation, not a fidelity match.
behind interaction and was not reproduced.
Merge order
This PR no longer blocks on the asset PR:
.assets-revisionpoints at the HF PR commit, so afresh clone fetches and builds today. The preferred order is still:
.assets-revisionto that merge commit (happy to push it, or fold it into the merge);Merging in the other order works too — the pin is valid either way, it just wants tidying
afterwards. Original contribution and full context: #29.
🤖 Generated with Claude Code