- _body() read the full declared Content-Length into memory before any size
check, so MAX_UPLOAD_BYTES could only reject an upload already held in
RAM. nginx caps this on the proxied path, but the container also listens
on the LAN, so the app now enforces its own 20MB ceiling and closes the
connection rather than reading.
- SQLite ran with the default rollback journal and 5s lock timeout, chosen
when a row was a few KB; rows now carry the original photos, so two
people grading at once could block each other's History. WAL + 30s.
- No socket timeout meant a stalled keep-alive connection held a worker
thread indefinitely.
- An edge excluded as a different material was dropped from the UI with no
explanation, presenting three sides as though they were all four.
Same failure family as the edge-whitening fix: centering_profile assumed a
uniform printed border, so on an E-X/SPx-style die-cut the side midpoint
samples landed on clear acetate and the border-width walk measured two
different materials, producing a confident 70/30 the model then treated as
authoritative. Now the four side samples must read as the same material or
the measurement refuses with the reason; the prompt tells the model to judge
die-cut centering as design-to-cut registration instead.
Stores the original photo(s) for every card graded from now on, so an
existing history row can be re-graded in place without re-uploading -- most
useful right after a grading-logic fix like the last few commits. Cards
graded before this feature (or saved without images) fall back to the same
photo picker as a fresh grade, then start getting instant regrades from then
on. list_grades/get_grade now select an explicit column list rather than
SELECT * so the stored images are never pulled into memory just to be
discarded for the browser-facing response.
The pooled baseline that scores edge whitening was global across all four
edges, so a card that's normal printed border on three sides and clear
acetate on the fourth had the three normal edges anchor a baseline that made
the clear side read as extreme whitening -- correctly detecting a real pixel
difference, just the wrong one. Now excludes an edge from both the pool and
its own scoring when its median brightness/saturation reads as a
fundamentally different material, and surfaces why in the prompt instead of
silently dropping it.
PWA card-grading app, deployed behind Nginx Proxy Manager on Unraid with
basic auth. Includes CARD_GRADER_BASE_PATH support for running under a
sub-path, and Docker/compose config for the Unraid deployment.