tools/render_check.py drives the real UI in Playwright's Chromium and asserts
what a person actually sees: the login gate, live search narrowing on a spec
value, the quick-adjust buttons, the category spec template pre-filling and
switching the unit to grams, the settings and password panels, and the phone
layout at a true 390px viewport.
Playwright's own Chromium rather than the installed Chrome: headless Chrome
returns nothing while a desktop Chrome is open, silently enough that it reads
as the app being broken.
It found a real bug on its first run. The fetch helper treated any 401 as a
lapsed session, so a wrong password bounced to "Not authenticated" instead of
saying the password was wrong. 401s from the login call itself are now left to
report their own reason.
It also confirmed the CSP is doing its job from the other direction:
Playwright's wait_for_function compiles predicates with eval() and is refused,
so the check polls from Python rather than the app loosening script-src.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Password management moves out of the CLI entirely. The credential is now a
salted scrypt hash in the database (so it survives rebuilds, living in the /data
volume) rather than an environment variable; PARTS_PASSWORD is demoted to a
bootstrap value that stops working the moment a password is set in the UI. Every
token carries a session epoch, so changing the password — or "sign out other
devices" — invalidates outstanding cookies while keeping the browser that made
the change signed in. A banner nags until the handed-over password is replaced.
app/admin.py remains for the one case the UI cannot cover, a forgotten password.
Audit findings:
- Taxonomy update and delete scanned affected parts before taking the write
lock, so a concurrent rename could leave the search index matching a name the
UI no longer showed. All four routes now lock first; removing the lock again
makes the new test fail exactly that way.
- History of a missing part returned 200 with an empty list; now 404.
- Infinity and NaN passed ge=0 and failed at the database. They are rejected as
422 now, and the validation error handler no longer chokes trying to echo a
non-finite value back.
Checks go from 134 to 181.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
FastAPI + SQLite behind a single-page frontend, deployed as a container on
mainserver behind Caddy.
One flexible parts table plus key/value specs so components, filament,
fasteners and tooling share a schema. Categories and locations are nestable
trees whose filters and sidebar counts both roll up through descendants.
Category spec templates pre-fill the properties worth recording for each kind
of part, which is what makes manual entry tolerable. Every quantity change is
logged. Search is FTS5 with prefix matching across names, MPNs, spec values,
tags, category and location.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>