snowspeederandClaude Fable 5 385474ecef
Test and deploy / test-and-deploy (push) Successful in 1m8s
Removable reference curves, audit fixes across pages, shaded Academy art
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]>
2026-08-11 16:59:28 -04:00

Roast Planner Webapp

A fillable, live-computing web version of the manual coffee roast plan worksheet (hope_roaster/roast-planner/manual-roast-planner.html), plus:

  • Prefill from a bean URL — paste a roaster's product page URL; the server fetches it and uses the Pi Coding Agent SDK (zero-tool, one-turn extraction) to pull out whatever it can find (cultivar, origin, processing, roast level), then deterministically maps that to worksheet field IDs using the same reference tables as the paper worksheet. It never invents a first-crack anchor or a modifier — those only ever come from the reference tables, keyed off what the page actually states.
  • Reference curve from an Artisan .alog — upload a real roast log (or browse a local library directory) to overlay its actual milestones/curve on the same SVG grid as your plan, dashed and in a different color.
  • Finished-roast log with Pi agent review — upload one or more finished Artisan .alog files against a plan (or standalone from /roasts); each is stored verbatim (downloadable as a backup), parsed, and deep-reviewed asynchronously by the same zero-tool Pi agent pattern as prefill (grade, highlights, concerns, next-batch suggestions, plan-vs-actual). The /roasts page lists every actual roast and renders its curve with the plan overlaid.
  • Brewing section — the app is split into Roasting and Brewing workflows in the nav. /beans manages roasted/purchased coffee (with LLM URL prefill from a roaster's product page); /brews logs every cup against a bean with a silhouette brewer picker (immersion / percolation / espresso & pressure — 13 methods), recipe fields (dose, water/yield, auto ratio, grind, temp, times), a 010 rating, and tasting notes. Bean remaining weight is derived from logged brew doses. Green inventory's lot form also prefills from a URL.
  • Plan chat + learned roaster behavior — an "Ask the LLM" drawer on the planner chats about the open plan, grounded in the computed ledger, the learned pace profile, and a roaster-behavior profile (/api/roaster-profile) aggregated from every uploaded .alog (turning-point lag, phase RoR, median milestone temps). The same profile feeds roast reviews and supplies the planner curve's fallback milestone temps.
  • API + tokens + Swagger — every capability (including admin) is a JSON endpoint, documented by a hand-authored OpenAPI 3 spec at /api/openapi.json and a self-hosted Swagger UI at /api-docs. Users generate bearer tokens (rpt_…) on the Account page; token requests skip CSRF (header-borne credentials can't be forged cross-site).
  • Backup — Admin → Backup exports the entire database as one JSON file and can import it back transactionally (delete-and-restore, refuses backups with no active admin, keeps the importing admin's session when possible). Users can download their own data from the Account page.
  • Academy/academy teaches roasting and brewing as narrated, animated slide lessons: green-bean physics through every roast stage (drying, Maillard, first crack, development, curve reading, defects) and extraction plus technique for every brewer family. Narration is Azure TTS (en-US-Andrew HD voice), pre-generated to committed MP3s by scripts/generate-academy-audio.mjs (reads AZURE_VOICE_KEY/AZURE_VOICE from .env, region eastus — run it locally after editing lesson scripts; production never needs the key). Slides carry display transcripts as captions and phonetic respellings for the voice.
  • Live ledger — the worksheet's time-ledger math (first crack, yellow, Maillard, development, drop, and the four sanity-check ratios) recomputes as you type, using the exact same arithmetic as the paper worksheet (verified against both its worked examples).
  • Print — bakes typed values into the print layout and reuses the paper worksheet's print CSS, so File > Print produces the same 2-page front/back sheet.

Run it

cp .env.example .env # set strong secrets
npm install
npm test
# local PostgreSQL stack (database is not published to the host)
docker compose --env-file .env up --build

The public landing page is at /; plans require an account at /app. Production is configured for https://roast.srmr.xyz: retain APP_ORIGIN=https://roast.srmr.xyz and COOKIE_SECURE=true behind its HTTPS proxy. Database migrations in db/migrations/ run at application startup exactly once.

First administrator

Generate BOOTSTRAP_SETUP_TOKEN with openssl rand -base64 48, keep it only in the deployment environment, then call POST /api/auth/bootstrap with that token, [email protected], and a 12+ character password. The endpoint can create that account only once. Remove the setup token after success; it is optional thereafter and no administrator password is stored in source control.

Pi agent configuration in Docker

The app service mounts PI_AGENT_CONFIG_DIR (default ./appdata/pi-agent) read-only at /run/pi-agent-config. Its entrypoint copies that seed into the non-root Node user's writable runtime configuration directory before startup: Pi's credential storage needs to create a lock beside auth.json. This lets /api/prefill use the configured model without baking credentials into the image or mutating the host configuration. The directory is ignored by Git and Docker build context; do not commit its contents.

Before bringing up the stack, sync only the local Pi agent configuration you intend to make available to the container:

mkdir -p appdata/pi-agent
rsync -a --delete ~/.pi/agent/ appdata/pi-agent/
docker compose --env-file .env up --build

Set PI_AGENT_CONFIG_DIR to another protected host directory instead if preferred. Restrict access to that directory because it can contain provider credentials. The mount is read-only, so Pi cannot alter the host configuration. If deployed behind a reverse proxy, set TRUST_PROXY only to that proxy's specific IP/CIDR (or keep it blank when the app is directly exposed).

Mobile and PWA use

The planner is responsive and caches its app shell for offline use after the first visit. Browser installation and service-worker caching require HTTPS in production (localhost is exempt). Put the Docker container behind an HTTPS reverse proxy before using it as an installable PWA on a phone.

Prefill requires a model configured via the pi CLI (~/.pi/agent/{models,auth}.json) — without one, /api/prefill returns 503 no_model rather than crashing. Everything else (the form, ledger, curve, .alog upload) works with no model configured. Which configured model runs prefill and roast reviews is chosen in the admin page's LLM section (app_settings.llm_model, "Auto" = first available); the UI refers to the model backend simply as "the LLM".

Layout

  • shared/ — browser-safe ESM modules imported by both the Node server and the browser (served statically at /shared/). This is the single source of truth for the ledger math, time parsing, and reference data tables — never duplicate this logic in server/ or public/js/.
  • server/ — Express routes: /api/prefill, /api/alog, /api/alog/library[/:filename].
  • public/ — the static frontend: index.html (the worksheet as a form), worksheet.css (a verbatim copy of the paper worksheet's print CSS — port changes from there, don't hand-edit divergently), app.css (screen-only chrome + form-control styling), js/.

Where the reference numbers come from

shared/reference-data.js is ported from /Users/shane/dev/hope_roaster/roast-planner/manual-roast-planner.html's Step 1 (cultivars), Step 3 (processing), Step 4 (roast levels), Step 8 (machine bands), and Step 7 (sanity bands). If those tables change in the paper worksheet, port the change here too.

Known gaps (v1)

  • Offline drafts are intentionally scoped to the authenticated browser account and are cleared on logout; account-backed plans remain the authoritative copy.
  • Roastetta (roastetta.com) integration is intentionally out of scope — it needs a headed, Cloudflare-clearing browser and the operator's own credentials. Use the .alog file picker, or point ALOG_DIR at wherever the roastetta skill already downloaded files.
S
Description
Roast Command Center
Readme
16 MiB
Languages
JavaScript 71.5%
HTML 19.9%
CSS 8.4%