The planner's Artisan .alog drawer now shows what's attached with a
"Remove reference curve" control (re-rendered per drawer open), so adding
a reference curve is no longer a one-way door; /api/alog shares the 8 MB
body cap so real-sized logs parse instead of failing with "bad_request".
Deep-evaluation fixes: editing a brew of an archived bean no longer
silently detaches the bean; the roasts pending-review poll no longer
wipes in-progress after-roast edits; roasters gain an Edit (rename/model)
action; the gear page refuses to autosave over a failed load; duplicating
a plan carries its custom name; cupping sessions can attach a plan after
creation (ownership-checked PUT + selector); admin user deletion also
refreshes plans/audit; cupping cup-count subtitle stays live; roasts
error-row colspan corrected. Regression tests cover the new cupping PUT
and the /api/alog body cap.
Academy scenes drop the flat paper-cutout look: shared defs provide
radial-gradient shading on every bean/half-bean/particle, flame gradients
with radiant halos, soft ground shadows, and a warm-lit stage background;
fill-shift animations now ride a partial-opacity tint overlay so shading
survives the color change.
Co-Authored-By: Claude Fable 5 <[email protected]>
One system: post-roast observations (weights, colour, DTR, cup notes,
'one change next batch', disproof) now live on the uploaded roast
(actual_roasts.after, migration 011), edited on the roast detail view
with weights/DTR prefilled from the .alog itself. The planner's screen
section is removed (the print worksheet keeps its hand-fill copy), and
the lot 'last refine' suggestion reads the note from both the new home
and legacy plans, newest wins.
Every roast now downloads two ways: the untouched original .alog, and
an updated supplemental copy with the app's data written back as valid
Artisan fields (weight, beans, roastingnotes incl. the LLM review,
cuppingnotes incl. linked cupping score/flavors) — serialized as a
Python literal so Artisan's ast.literal_eval re-imports it.
Co-Authored-By: Claude Fable 5 <[email protected]>
Machines:
- roasters table + actual_roasts.roaster_id (migrations 009): per-user
machines with a default; uploads attach to the default roaster and are
reassignable from the roast detail view
- Learning is now per machine: /api/roaster-profile scopes to a roaster
(default when unspecified) and applies the user's override tweaks
(milestone temps, TP time, pace factor) on top of learned medians
- /roasters page: machine list + analysis report showing learned vs
applied values with editable tweaks, typical RoR, and a plain-language
list of the adjustments applied to plans; planner honors a manual pace
override; evaluations use the roast's own machine profile
Mobile header redesign:
- The planner's header tools collapse behind a single 'Tools ▾'
disclosure (dropdown card) at ≤720px, keeping the fixed three-row
mobile header with no wrapped or scrolling button rows; desktop
renders inline via display:contents (same DOM, same handlers)
Profile pictures:
- users.avatar (migration 010) + PUT/GET/DELETE /api/account/avatar;
client-side centre-crop resize to 128px JPEG; avatar shows in the nav
chip on every page; included in full backups
Co-Authored-By: Claude Fable 5 <[email protected]>
- user_gear table (migration 008) + GET/PUT /api/gear (brewer keys
validated against the taxonomy, grinders deduped/trimmed)
- /gear page: silhouette multi-select for owned brewers (autosaves) and
a grinder list; nav gains Brewing → Equipment
- Brews page: with gear configured, the picker shows only owned brewers
(plus the edited brew's method) with a show-all toggle; grinder field
suggests your grinders; empty-gear state links to /gear
- Included in full backups, per-user export, and the OpenAPI spec
Co-Authored-By: Claude Fable 5 <[email protected]>
The extraction schema gains cultivarGroup (typica/bourbon/ethiopian/
hybrid, classified by the LLM from coffee genetics); when the named
variety isn't in the cultivar table, deriveFields now falls back to the
group's first-crack anchor (number still comes only from the reference
table) instead of leaving 1.4 blank and breaking every downstream
calculation. deriveFields exported + unit-tested.
Co-Authored-By: Claude Fable 5 <[email protected]>
Free-text recipe (bloom, pours, timings) distinct from tasting notes and
next-time notes: new brews.recipe column (migration 007), form textarea,
shown under the recipe summary in brew history, carried by Brew again,
included in backups and the OpenAPI spec.
Co-Authored-By: Claude Fable 5 <[email protected]>
Brewing:
- roasted_beans + brews tables; /beans (bean management with LLM URL
prefill) and /brews (silhouette brewer picker across immersion/
percolation/espresso, recipe fields, auto ratio, 0-10 rating, tasting
notes); bean remaining weight derived from logged brew doses
- Green inventory lot form also prefills from a product URL
Navigation/UX:
- Side nav is now generated from one definition in nav.js, grouped
Roasting / Brewing / account, consistent on every page
LLM:
- 'Ask the LLM' chat drawer on the planner (stateless /api/plan-chat)
grounded in the plan, computed ledger, learned pace, and a new
roaster-behavior profile aggregated from uploaded .alogs
(/api/roaster-profile: TP lag, phase RoR, median milestone temps)
- The profile also feeds roast reviews and the planner curve's fallback
milestone temps
API platform:
- User-generated bearer tokens (rpt_…) with account-page management;
token requests skip CSRF; hand-authored OpenAPI 3 spec at
/api/openapi.json rendered by self-hosted Swagger UI at /api-docs
- Full-database backup export/import (admin) + per-user data export
Co-Authored-By: Claude Fable 5 <[email protected]>
- New server/llm.js consolidates ModelRuntime + model selection: admin
setting (app_settings.llm_model) > LLM_MODEL/PREFILL_MODEL env > first
available; used by both prefill and roast evaluation
- GET/PUT /api/admin/llm lists configured models and stores the choice
(validated against the list; empty = auto; audited); admin page gains
an LLM section with a model picker
- All user-facing 'Pi agent' wording is now 'LLM'; no_model error message
no longer references the pi CLI
- /roasts table: Review column removed (review lives in the detail view)
Co-Authored-By: Claude Fable 5 <[email protected]>
- POST /api/roasts stores the original .alog verbatim (multiple per plan),
parses it server-side, and kicks off an async zero-tool Pi agent review
(grade, highlights, concerns, next-batch suggestions, plan-vs-actual)
- /roasts page: table of historical actual roasts with review summaries;
row detail renders the BT curve as SVG with the plan curve overlaid,
shows the full review, and offers original-.alog backup download,
re-evaluate, and delete
- Planner's Reference curve drawer gains a multi-file finished-roast
uploader that attaches to the open (synced) plan
- Failed reviews record the error on the row and are retryable via
POST /api/roasts/:id/evaluate (e.g. once a model is configured)
Co-Authored-By: Claude Fable 5 <[email protected]>
Replaces the flat additive batch-size correction with a per-user
multiplicative pace factor learned from each account's own logged
roasts (shared/learn.js, GET /api/machine-profile), plus a lot-scoped
"last refine" auto-suggestion. Also fixes the reference data being
framed as "your own roasts" on a now-multi-user product, and
reclassifies the drying/Maillard sanity checks as informational since
they're algebraically derived from the DTR check rather than
independent (yellow = 0.56 x first crack, not entered separately).
Bug fixes found by an adversarial Opus review of the first pass:
- Unicode minus sign (U+2212) broke duration parsing against the
app's own generated refine-suggestion text
- Printed sanity-checks table still showed a bare pass/fail glyph for
the now-informational drying/Maillard rows
- Printed time-ledger box didn't show the pace multiplication step,
so it stopped reconciling by hand once pace != 1
- Field 6.4 (manual batch correction) was double-counted: excluded
from the learned-pace fit but added back after the multiplication
- Learned pace had no outlier rejection or hard clamp
- computeLedger had no test coverage
- Batch-size help copy overstated what the pace factor models (it's
a single blanket ratio, not conditioned on batch weight)
A follow-up Opus pass also caught the per-user profile cache
surviving logout/account-switch in a shared browser; fixed by
sweeping it alongside the existing plan-draft cleanup.
Co-Authored-By: Claude Sonnet 5 <[email protected]>
Ports inventory management and SCA-style cupping scoring from
hope_roaster: lot tracking with audit-logged consumption, cupping
sessions with server-authoritative scoring and a live radar chart,
and links from the planner (draw-from-lot, open-cupping-session).
Also fixes the Blend/Single-origin toggle layout, replaces tooltips
with an in-context "why" teaching layer, and stages the planner UI
into pre-roast vs. post-roast phases.