Calibrate every head, unify the chip language, fix the modal layout - #269
Merged
Merged
Conversation
…yout Closes #265, #266, #267, #268. CALIBRATION EVERYWHERE (#265). Taste and Tox21 still ran on a flat 0.5 with only AUROC shown — the exact reporting gap #261 closed for aroma and mouthfeel. Both now use the same out-of-fold calibration with a 50% precision floor, and /api/heads publishes threshold, precision, recall and confident_capable for all four modalities. The taste heads were mildly shy, as expected with hundreds of positives each (sweet 0.33, umami 0.33, bitter 0.51), and all six clear the floor at 0.50-0.87. The Tox21 result is the one that matters: every one of the twelve calibrated UPWARD — NR-AR to 0.69, NR-AR-LBD 0.65, NR-ER 0.63 — which means a flat 0.5 had them OVER-flagging. That is the opposite direction from the thin aroma heads and the more dangerous one, because a caution flag that cries wolf teaches people to ignore the flags that matter. CHIP LANGUAGE (#266). Flavor, note, taste and mouthfeel chips had each been given a different border STYLE at a different time — dashed, dotted, thicker, plus per-taste text recolouring — so they read as four accidents rather than one system. They now share a pill, a size and a border weight, and carry identity in a leading dot in the family's own hue. Each studio section also gained a plain-language paragraph on what that dimension IS and why it matters: a flavor is the whole percept, a note is what the nose reads, taste is the basics the tongue reads, mouthfeel is touch reported by the trigeminal nerve. Menthol is cold without a temperature change; that distinction is load-bearing and nothing in the UI had ever said it. ALL MODALITIES ON CARDS (#267). Substitute and neighbor cards showed taste and aroma but not mouthfeel. Read straight off the profile matrix, whose mouthfeel columns were already there, so it costs no extra inference — and each sensation must clear its own calibrated threshold, so a card never shows something the modal would call indicative. MODAL LAYOUT (#268). Two real bugs, both measured rather than guessed: - Mobile: the swap grid used `1fr`, which is minmax(auto,1fr), and `auto` will not shrink below min-content — so the substitutes and neighbors cards rendered 468px wide inside a 358px modal. minmax(0,1fr) fixes it; .modal-top already had this and .swap-grid did not. - Desktop: #results is a flex column whose 14px gap separates the cards, but the render path set an inline display:block on it, and block boxes ignore `gap`. The Heads card had been sitting flush against the Substitutes card below it. Clearing the inline style lets the stylesheet win. Verified with a Playwright probe at 390px and 1440px: no card overflows its container at either size, and no two cards are within 8px of each other on desktop. Signed-off-by: Austin L. <86896075+rvnminers-A-and-N@users.noreply.github.com>
HOW-IT-WORKS.md used CV-AUROC eleven times and never said what it is beyond a parenthetical, and never mentioned threshold, precision or recall at all — the real explanation lived only in ACCURACY.md, one click further than most readers go. A six-term glossary now sits at section 0.5, before any number is used: head, AUROC (0.5 is a coin flip, 1.0 is never wrong), threshold (not a flat 0.5 — 0.16 to 0.85, fitted per head), precision (when it says yes, how often is it right), recall, out-of-fold, and confident vs indicative. Two sentences each. The same glossary, compressed to a definition list, is now a card in the site's own How-it-works grid, so it reaches someone who will never open the docs folder. Also tightens the taste wording, which was loose in both places: there are FIVE basic tastes and SIX taste heads. `tasteless` is a head that predicts the documented absence of taste — genuinely useful, since it is how you find a clean carrier — but it is not a sixth basic taste, and calling it one is the kind of small inaccuracy a flavorist would notice immediately. Signed-off-by: Austin L. <86896075+rvnminers-A-and-N@users.noreply.github.com>
…xcluded Three stack questions kept resurfacing, so they are now answered in the one place someone would look, with the reasoning rather than just the verdict. Frontend is pinned to React 19 + Vite + TypeScript with Tailwind and shadcn/ui, explicitly NOT MUI. shadcn is copy-in rather than import, so the components land in our own source tree where they can be read and owned — which for a portfolio is the difference between showing you used a component library and showing you can build one. Flavormancer also already has a visual identity (the purple/teal gradient, the gilded scroll rods, the flask loader) that Material would flatten, and the port would be spent overriding it. The trade is stated honestly: MUI wins when you need an enterprise data-grid and date pickers tomorrow. This app is cards, chips, charts and a modal. Node.js is scoped to build tooling and an optional TypeScript MCP server beside the Python one — never a third backend. Two API stacks is breadth; three is sprawl, and it reads as indecision. Laravel is deliberately absent and the reasoning is written down so nobody re-opens it: it is a good fit for CRUD-and-content products and is used heavily elsewhere in this portfolio, but Flavormancer is on-prem scientific computing. Adding a familiar framework that proves nothing new would blur that. Choosing against your most comfortable stack when it does not fit is the point. Signed-off-by: Austin L. <86896075+rvnminers-A-and-N@users.noreply.github.com>
This was referenced Jul 30, 2026
rvnminers-A-and-N
added a commit
that referenced
this pull request
Jul 30, 2026
…oc (#229, #26, #25) (#273) CHIP LEGEND (#229). Four dimensions with four different meanings sit side by side in the studios, and after the chip-system work in #269 the dot colour was the only thing distinguishing them — which works only if you already know the code. A key now states it once, up front: what each family means, and what solid / outline / hatched marking indicates (documented, predicted, indicative). The hatched swatch matters most: it is the one that says "right less than half the time — a hint, not an answer". MEASURED PROPERTIES (#209, in progress). 73% of the enrichment table had no measured boiling point, and the assumption was that the data did not exist. It does. build_properties.py reads PubChem's property table, which only ever carries COMPUTED values; measured boiling and melting points live in PUG-View under Experimental Properties, an endpoint this codebase had never called. A sample of molecules with no BP found 8 of 8 had a PubChem record. build_measured_properties.py crawls that endpoint. Parsing is the real work — values are free text written by whoever took the measurement ("246 °C", "115-116 °C at 12 mm Hg", "410 °F") — so it converts Fahrenheit, takes the midpoint of a range, and DISCARDS anything measured at reduced pressure. A boiling point at 12 mmHg is not comparable to one at atmospheric, and silently mixing them would corrupt the volatility ordering the formulation studio depends on. properties.parquet already carries boiling_point_pressure_mmhg for exactly this reason. Smoke test resolved 11 of 12 with values that check out (pyrrolidine 86.6 °C). The full crawl is ~7,200 molecules at roughly 57% hit rate and takes hours; it checkpoints every 100 so it can be folded in whenever it finishes. _by_skel() would have silently discarded all of it. It required an `inchikey` column, but both backfill tables are keyed by `inchikey_skel` — so it returned an empty dict, with no error, and every backfilled name and boiling point would simply never have appeared. It now accepts either. DATA-PIPELINE.md (#26, #25). What every build script reads, the REAL column names in each table read off the built artifacts rather than copied from the loader's hopes, and a clean-machine first run in dependency order with timings and an end-to-end verification that checks a prediction rather than just that the server started. Two claims in the first draft were wrong and are corrected: the verification example printed `.aroma.top[0]`, which is the highest-scoring CONFIDENT head and not the one you searched for; and I had written that `n_sources` breaks label conflicts by weight of evidence. It does not — nothing reads it, and conflicts resolve POSITIVE-WINS: one source calling a molecule sweet makes it sweet. That is the right default for sparse partially-annotated sources, but it means a single mislabelled source can assert a taste the others deny, which is worth knowing and is now written down. Also resolves the [OBTAIN]/[VERIFY] markers: flavordb_taste.csv is permanently excluded (NonCommercial), umami_list.csv is an unused nice-to-have, sweeteners_db.csv is present and drives the intensity regressor. Signed-off-by: Austin L. <86896075+rvnminers-A-and-N@users.noreply.github.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #265, #266, #267, #268.
Calibration everywhere (#265)
Taste and Tox21 still ran on a flat 0.5 with only AUROC shown — the exact gap #261 closed for aroma and mouthfeel. Both now use the same out-of-fold calibration with a 50% precision floor, and
/api/headspublishesthreshold,precision,recallandconfident_capablefor all four modalities.Taste heads were mildly shy, as expected with hundreds of positives each —
sweet0.33,umami0.33,bitter0.51 — and all six clear the floor at 0.50–0.87.The Tox21 result is the one that matters. All twelve calibrated upward (NR-AR → 0.69, NR-AR-LBD → 0.65, NR-ER → 0.63), which means a flat 0.5 had them over-flagging. That's the opposite direction from the thin aroma heads and the more dangerous one: a caution flag that cries wolf teaches people to ignore the flags that matter.
Chip language + what each dimension IS (#266)
The four chip families had each been given a different border style at a different time — dashed, dotted, thicker, plus per-taste text recolouring — so they read as four accidents rather than one system. They now share a pill, size and border weight, with identity carried by a leading dot in the family's hue.
Each studio section gained a plain-language paragraph on what its dimension is and why it matters. Menthol is cold with no temperature change; capsaicin burns with no heat. That distinction is load-bearing and nothing in the UI had ever said it.
All modalities on cards (#267)
Substitute and neighbor cards showed taste and aroma but not mouthfeel. Read straight off the profile matrix — the mouthfeel columns were already there, so it costs no extra inference — and each sensation must clear its own calibrated threshold, so a card never shows something the modal would call indicative.
Modal layout (#268) — both bugs measured, not guessed
.swap-gridused1fr, which isminmax(auto,1fr), andautowon't shrink below min-content — so the cards rendered 468px wide inside a 358px modal..modal-topalready usedminmax(0,1fr);.swap-griddidn't.display:blockon#results, and block boxes ignoregap— so the stylesheet'sgap:14pxhad never applied and the Heads card sat flush against Substitutes.Playwright probe at 390px and 1440px: no card overflows its container at either size, no two cards within 8px on desktop.
Glossary
HOW-IT-WORKS.mdused CV-AUROC eleven times without defining it, and never mentioned threshold, precision or recall. A six-term glossary now sits at §0.5 before any number is used, mirrored as a card in the site's own How-it-works grid.Also: five basic tastes, six taste heads —
tastelesspredicts documented absence of taste (how you find a clean carrier), not a sixth basic. Loose in both places before.Verified: ruff clean, 43 tests passing. Needs
sudo systemctl restart flavormancer— models changed.