Add per-user learned machine profile; fix methodology honesty issues
Test and deploy / test-and-deploy (push) Successful in 1m26s
Test and deploy / test-and-deploy (push) Successful in 1m26s
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]>
This commit is contained in:
+58
-11
@@ -499,7 +499,17 @@
|
||||
>0 the first time you roast this coffee. Afterwards,
|
||||
carry "One change next batch" here — this is how the
|
||||
plan learns.</span
|
||||
></label
|
||||
>
|
||||
<div class="refine-suggestion hidden" id="refine-suggestion">
|
||||
<span id="refine-suggestion-text"></span>
|
||||
<button
|
||||
type="button"
|
||||
class="ghost-btn small hidden"
|
||||
id="btn-apply-refine"
|
||||
>
|
||||
Apply
|
||||
</button>
|
||||
</div></label
|
||||
>
|
||||
<label class="field" data-fid="1.7"
|
||||
><span class="field-label">± Dev modifier</span
|
||||
@@ -1242,6 +1252,7 @@
|
||||
Drying + Maillard + Development = the whole roast. Cultivar
|
||||
sets first crack; process and level set development.
|
||||
</p>
|
||||
<p class="dock-note machine-profile-note" id="machine-profile-note"></p>
|
||||
|
||||
<div class="ledger-group">
|
||||
<div class="ledger-row">
|
||||
@@ -1270,6 +1281,16 @@
|
||||
title="225 g lands first crack ~1:30–2:00 later than 200 g"
|
||||
/>
|
||||
</div>
|
||||
<div class="ledger-row ledger-subtotal">
|
||||
<span class="l-fid"></span
|
||||
><span class="l-label">Subtotal (before pace)</span
|
||||
><output class="l-val" data-out="subtotalA">—</output>
|
||||
</div>
|
||||
<div class="ledger-row ledger-pace">
|
||||
<span class="l-fid"></span
|
||||
><span class="l-label">× Machine pace</span
|
||||
><output class="l-val" data-out="pace-factor">×1.00</output>
|
||||
</div>
|
||||
</div>
|
||||
<div class="ledger-total hero">
|
||||
<span>First crack</span><output data-out="A">—</output>
|
||||
@@ -1325,19 +1346,22 @@
|
||||
<div class="check-tile unknown" data-pass="drying">
|
||||
<span class="check-name">Drying share</span>
|
||||
<output class="check-value" data-out="check-drying">—</output>
|
||||
<span class="check-band">target 43–51%</span>
|
||||
<span class="check-band">informational, target 43–51%</span>
|
||||
<span class="check-why"
|
||||
>high = late yellow, browning starved · low = rushed
|
||||
before internal pressure built</span
|
||||
>Derived from the development ratio (yellow is 0.56 ×
|
||||
first crack, not entered independently) — this can't fail
|
||||
on its own. High = late yellow, browning starved · low =
|
||||
rushed before internal pressure built.</span
|
||||
>
|
||||
</div>
|
||||
<div class="check-tile unknown" data-pass="maillard">
|
||||
<span class="check-name">Maillard share</span>
|
||||
<output class="check-value" data-out="check-maillard">—</output>
|
||||
<span class="check-band">target 33–39%</span>
|
||||
<span class="check-band">informational, target 33–39%</span>
|
||||
<span class="check-why"
|
||||
>low = too small a browning window for sweetness and
|
||||
complexity</span
|
||||
>Same derivation as drying share — informational, not an
|
||||
independent check. Low = too small a browning window for
|
||||
sweetness and complexity.</span
|
||||
>
|
||||
</div>
|
||||
<div class="check-tile unknown" data-pass="dtr">
|
||||
@@ -1878,6 +1902,22 @@
|
||||
/><span class="pv"></span>
|
||||
</td>
|
||||
</tr>
|
||||
<tr class="row">
|
||||
<td class="ln"></td>
|
||||
<td class="desc">Subtotal (before pace)</td>
|
||||
<td class="val"><output data-out="subtotalA">—</output></td>
|
||||
</tr>
|
||||
<tr class="row">
|
||||
<td class="ln"></td>
|
||||
<td class="desc">
|
||||
<span class="op">×</span> Machine pace
|
||||
<span class="sub"
|
||||
>learned from your own roasts, else ×1.00
|
||||
(reference)</span
|
||||
>
|
||||
</td>
|
||||
<td class="val"><output data-out="pace-factor">×1.00</output></td>
|
||||
</tr>
|
||||
<tr class="total">
|
||||
<td class="ln">A</td>
|
||||
<td class="desc"><strong>FIRST CRACK</strong></td>
|
||||
@@ -1964,16 +2004,16 @@
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>Drying share</td>
|
||||
<td>Drying share (informational)</td>
|
||||
<td class="num">43–51 %</td>
|
||||
<td class="num">
|
||||
<output data-out="check-drying">—</output>
|
||||
</td>
|
||||
<td class="num passcell unknown" data-pass="drying"></td>
|
||||
<td>high = late yellow · low = rushed</td>
|
||||
<td>can't fail on its own — see * below</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Maillard share</td>
|
||||
<td>Maillard share (informational)</td>
|
||||
<td class="num">33–39 %</td>
|
||||
<td class="num">
|
||||
<output data-out="check-maillard">—</output>
|
||||
@@ -1982,7 +2022,7 @@
|
||||
class="num passcell unknown"
|
||||
data-pass="maillard"
|
||||
></td>
|
||||
<td>low = too little browning</td>
|
||||
<td>can't fail on its own — see * below</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Development ratio</td>
|
||||
@@ -2004,6 +2044,13 @@
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<p class="wsfoot">
|
||||
* Drying share and Maillard share are always in-band
|
||||
whenever Development ratio passes — yellow is calculated as
|
||||
a fixed 56% of first crack, not measured independently, so
|
||||
these two rows can't fail on their own. Only Development
|
||||
ratio and Development ceiling are real pass/fail checks.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
Reference in New Issue
Block a user