Add per-user learned machine profile; fix methodology honesty issues
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:
2026-07-31 12:40:03 -04:00
co-authored by Claude Sonnet 5
parent 0a0356b86e
commit 2237c199c1
14 changed files with 999 additions and 83 deletions
+26
View File
@@ -1625,6 +1625,28 @@ body.show-fids .field[data-fid]::after {
.check-tile.fail .check-why {
display: block;
}
.check-tile.informational .check-why {
display: block;
}
.check-tile.informational .check-band {
font-style: italic;
}
.refine-suggestion {
margin-top: 6px;
padding: 8px 10px;
border-radius: var(--radius-sm);
border: 1px solid var(--ember-soft);
background: var(--ember-soft);
display: flex;
flex-direction: column;
align-items: flex-start;
gap: 6px;
}
.refine-suggestion span {
font-size: 11.5px;
color: var(--ink-2);
line-height: 1.4;
}
.dock-note {
font-size: 10.5px;
color: var(--ink-3);
@@ -2796,6 +2818,10 @@ body.show-fids .field[data-fid]::after {
td.passcell.fail::before {
content: "✗ ";
}
td.passcell.informational::before {
content: "— ";
color: var(--mid);
}
}
/* ─── Legacy .opt styling reused by the (hidden-on-screen) print worksheet's own radios ── */