Skip to content

chore(deps): bump androidx.wear.protolayout:protolayout from 1.3.0 to 1.4.2 - #20

Open
dependabot[bot] wants to merge 800 commits into
devfrom
dependabot/gradle/dev/androidx.wear.protolayout-protolayout-1.4.2
Open

dependabot[bot] wants to merge 800 commits into
devfrom
dependabot/gradle/dev/androidx.wear.protolayout-protolayout-1.4.2

Conversation

@dependabot

@dependabot dependabot Bot commented on behalf of github Sep 3, 2026 •

Copy link
Copy Markdown
Contributor

Bumps androidx.wear.protolayout:protolayout from 1.3.0 to 1.4.2.

Dependabot compatibility score

You can trigger a rebase of this PR by commenting @dependabot rebase.


Dependabot commands and options

You can trigger Dependabot actions by commenting on this PR:

  • @dependabot rebase will rebase this PR
  • @dependabot recreate will recreate this PR, overwriting any edits that have been made to it
  • @dependabot show <dependency name> ignore conditions will show all of the ignore conditions of the specified dependency
  • @dependabot ignore this major version will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself)
  • @dependabot ignore this minor version will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself)
  • @dependabot ignore this dependency will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)

Note
Automatic rebases have been disabled on this pull request as it has been open for over 30 days.

tim2000s and others added 30 commits July 20, 2026 16:17
The antBackout= shadow tag gained a 10th field, armSrc, recording which trigger
armed the run — the accelMeal onset detector or the mlMealLikely placeholder
(2026-07-20 ACCELMEAL_ARM). Adds antbackout_armsrc (text) to the parse dict + DDL;
absent in pre-accelArm data -> None (index-guarded). Verified on new + old tags.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015tCPKdr97rkLLuR7FGL6TU
…store + guard follow-through

Answers Tim: did V6 lose V1's acceleration response, and is it safe to re-add?

- V1's delta_accl>10 gate leads V6 CONFIRMED by median 15 min at 98% recall (precision
  15%) — a real early detector V6 gated away (v1_accel_lead.py, 14,430 fires).
- V1's early bolus is small (median 0.25U) and FIZZLE-SAFE BY SIZE. First pass looked
  mixed (raw fizzle-low 12.7%, D/tim ~28%) but that mis-attributed downstream SMBs to
  the entry. Allocating a low to the fizzle bolus only when subsequent insulin <= the
  bolus (v1_fizzle_pure.py): pure fizzle-low 4.4% vs 3.3% ambient, Delta +0.9%
  [-0.6,+3.0] — no excess. 69% of the raw fizzle-lows were DOWNSTREAM follow-through,
  not the entry (D/tim 70-77% downstream). Residual: C small pure excess, tim mildly
  elevated (U200 = 2x mass) -> light per-user touch.

REINTEGRATION_SPEC.md: restore V1's early acceleration bolus as a small fizzle-safe
PRIMER in OBSERVING, netted OFF the CONFIRMED commit-shot (move-not-add = harm-neutral),
with V6's existing brake/RECOVERING guarding the follow-through V1 lacked (which is what
crashed V1). Auto-config-sized (well-controlled default; C/tim/U200 smaller; most
hypo-prone -> antBackout temp-basal). Shadow-first, two-test bar.

Registered: finding (Dosing timing) + design (Partial/unbuilt).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015tCPKdr97rkLLuR7FGL6TU
…eal engine

Reclaims V1's ~15-min-earlier acceleration response (backtesting/scripts/
2026-07-v1-acceleration: delta_accl>10 leads V6 CONFIRMED by 15 min at 98% recall;
the early bolus is fizzle-safe by size, pure fizzle-low +0.9% no excess). Restores it
as a small primer during OBSERVING, delivered as an advance on the CONFIRMED
commit-shot.

Mechanics (DetermineBasalBoostV5.decide()):
- ONCE per OBSERVING session, on delta_accl>10 + rising + every floor clear (recentLow
  >=80, awake, !exercise, !post-rescue) + maxIOB headroom. Acceleration-scaled
  (1 + (accl-10)/20, capped 2x base = inside V1's proven fizzle-safe envelope).
- Bolus mode: folded into finalDose, exempt from the non-meal v1-cap at the seam.
- ADDITIVE up to the fizzle-safe base (primerCapU); the scaled EXCESS is netted off the
  CONFIRMED shot then COMMITTED holds (move-not-add; net-extra converges to the base).
- Session accumulators (primerAppliedU, netting residual) in V5PersistedState, persisted
  in V5StateStore, reset on IDLE. The follow-through that crashed V1 is guarded by V6's
  existing brake/RECOVERING.

Routing (auto-config managed): well-controlled -> bolus; everyone else -> RETRACTABLE
temp-basal fallback (additive-only raise, never cancels a protective zero-temp, expires
= retracts). USER OVERRIDE ApsBoostV5PrimerBolusMode forces the bolus. primerCapU sized
from the user's own routine SMB (U200 auto-scaled), smaller for hypo-prone (C/tim
residual). Keys ApsBoostV5PrimerCapU/PrimerTbrFallback/PrimerBolusMode + UI + strings.

8 unit tests (PrimerTest): fires-in-observing/scaled/additive/residual, floors gate,
no-accel, temp-basal-not-folded, once-per-session, netting subtracted, IDLE reset.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015tCPKdr97rkLLuR7FGL6TU
applyAutoConfig() writes each managed double via an EXPLICIT resolve() call; PrimerCapU
was added to managedDoubleKeys/Knobs (so the derivation runs) but its resolve() call
was missing, so the cap was never persisted and never resolved — it stayed at 0 and
re-derived uselessly every cycle. The tbr-fallback BOOLEAN applied (boolean path loops
the list), so a tbr-routed user got the routing but no cap → primer delivered nothing.
Caught by Tim on-device.

Add the resolve() call (NOT raise-guarded — a tbr-routed non-well-controlled user still
needs a non-zero cap to size the retractable temp-basal). Update the exact-key-list test
+ add a regression guard asserting PrimerCapU is applied and > 0.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015tCPKdr97rkLLuR7FGL6TU
… — NOT supported

Answers Tim: the founding Boost flow (seed a maybe-meal early, dose enough to trigger
UAM, firm up on behaviour). V6 replaced it with a gated commit-shot. Should we bring it
back? Validate-before-building says no (broadly).

founding_flow.py (8 users both eras, 3865 real-meal seeds, within-user cluster-boot):
V1 and V6 deliver statistically the same insulin, at the same speed (first dose ~15min),
reaching the SAME peak (183=183) with the same lows. The 15-min "lead" is in the CONFIRM
label, not delivery (V6's OBSERVING doses at V1's base throughout). Per-user heterogeneous
— V6 lags for tim/E/H, leads for A/B — so it's real for tim but not universal.

overblock_meals.py (V6 high acceleration-meals, split by confirm-in-20min): 53% were
over-blocked, yet the WITHHELD meals peaked LOWER (193 vs 207) with FEWER lows (8.4 vs
12.4%) than the aggressively-dosed ones, and below V1. Aggressive-early (= the founding
flow) associates with MORE lows. Selection-confounded by meal size (gate confirms the big
meals), so not proof V6 is optimal — but no evidence over-blocking costs outcomes.

Conclusion: do NOT build the graded-ramp firm-up. Keep the primer as built (small, netted,
per-user) for the lag-users incl. tim; don't escalate to an aggressive ramp. Consistent
with H12 (V1≈V6 net) + recovering-highs/high-IOB-tail. Registered (Discarded).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015tCPKdr97rkLLuR7FGL6TU
…ainst the commit-shot

Tim's rule (tested: the one lever that survives — cap/taper both trade seeds 1:1, this
loses none). Keep every primer firing, but at CONFIRM net the ACCUMULATED primer IOB
beyond one base off the commit-shot, so the meal's net-extra insulin is bounded to one
base no matter how many fizzles preceded it (backtesting: 87% of confirms follow >=1
primer, 57% would net off >0.1U, median 0.39U).

- V5PersistedState: primerIobU (cross-session on-board primer insulin, wall-clock decayed
  exp(-dt/90min), NOT reset on IDLE) + primerIobUpdatedMs. V5Inputs.nowMs (dateUtil.now()).
- decide(): accumulate every primer into primerIobU; at the CONFIRMED transition set the
  netting residual = max(0, primerIobU - primerCap) and consume the credited excess
  (primerIobU := min(primerIobU, cap)) so a second meal can't re-credit it. Residual spent
  down against CONFIRMED then COMMITTED holds (unchanged landing). Replaces the old
  session-scoped scaled-excess netting.
- Only ever REMOVES insulin (safe-signed). Does NOT touch pure-fizzle clusters that never
  confirm (their mild intermediate dip is unaddressed, by design).
- V5StateStore persists the two new fields. PrimerTest updated + confirm-net test added.

backtesting/scripts/2026-07-primer-clustering (clustering, cap-sweep, taper, confirm-net)
to follow.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015tCPKdr97rkLLuR7FGL6TU
Replays the live primer over all V6-era history to size the fizzle-clustering and test
three constraints (backtesting/scripts/2026-07-primer-clustering/):
- clustering: ~12 fires/day, 73% fizzle, 84% clustered; only 6% of clusters dip<80
  (D 23%, tim 9%). Frequent but mostly benign; self-suppresses the primer on real meals.
- rolling-window cap: ~1:1 seed:fizzle trade at every W×K (seeds interleaved with fizzles,
  meals start after fizzle bursts) — REJECTED.
- IOB taper: cuts dips only proportionally to cutting the dose incl. real-meal seeds
  (cap 0.5 -> dips -37%, seed -41%) — REJECTED (≈ smaller global primer).
- confirm-net (Tim's rule): 87% of confirms follow >=1 primer, nets off >0.1U on 57%
  (median 0.39U); loses no seeds, bounds meal net-extra to one base, safe-signed —
  SHIPPED 6c439c8.

Registered: confirm-net (Found/used), cap + taper (Discarded).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015tCPKdr97rkLLuR7FGL6TU
…se (CRITICAL safety)

Incident: during zone-3 exercise (HR 104 bpm, HRR 42%) with 64 steps in the
trailing windows, the loop classified RESTING, fired the INACTIVE branch and
raised the profile to 130% while BG was falling 12 mg/dL per 5 min. Two defects
compounded:

1. Classifier dead zone — the RESISTANCE branch required lowSteps (<30), so 30-99
   steps + zone 3/4 HR matched no branch and fell through to RESTING. Widened to
   !moderateSteps (<100 steps), zone 3-4 → RESISTANCE. (HrActivityCalculator)

2. INACTIVE branch ignored an elevated HR — it only checked STRESS before adding
   insulin. Added a belt-and-braces guard keyed on the HR ZONE directly
   (inactivitySuppressedByElevatedHr, zone >= 3): when it holds, suppress the
   profile-raise entirely and raise the target to 160 instead. Never add basal
   while the heart says exercise, whatever the fused state says. (OpenAPSBoostPlugin)

Tests: dead-zone regression (64 steps + zone3 → RESISTANCE), suppression helper
(zone3+ true / zone1-2 / null false), and the RESTING-fallback test repointed to
a genuinely-resting input (its old input was the buggy behaviour).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015tCPKdr97rkLLuR7FGL6TU
…se (CRITICAL safety)

Incident: during zone-3 exercise (HR 104 bpm, HRR 42%) with 64 steps in the
trailing windows, the loop classified RESTING, fired the INACTIVE branch and
raised the profile to 130% while BG was falling 12 mg/dL per 5 min. Two defects
compounded:

1. Classifier dead zone — the RESISTANCE branch required lowSteps (<30), so 30-99
   steps + zone 3/4 HR matched no branch and fell through to RESTING. Widened to
   !moderateSteps (<100 steps), zone 3-4 → RESISTANCE. (HrActivityCalculator)

2. INACTIVE branch ignored an elevated HR — it only checked STRESS before adding
   insulin. Added a belt-and-braces guard keyed on the HR ZONE directly
   (inactivitySuppressedByElevatedHr, zone >= 3): when it holds, suppress the
   profile-raise entirely and raise the target to 160 instead. Never add basal
   while the heart says exercise, whatever the fused state says. (OpenAPSBoostPlugin)

Tests: dead-zone regression (64 steps + zone3 → RESISTANCE), suppression helper
(zone3+ true / zone1-2 / null false), and the RESTING-fallback test repointed to
a genuinely-resting input (its old input was the buggy behaviour).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015tCPKdr97rkLLuR7FGL6TU
(cherry picked from commit 12b589d)
…onfirm-crash hole

2026-07-23 user-H incident (twice in one afternoon, loop then disabled by
the user): post-rescue tier block demotes T3/T5 to T7/T8, but T7/T8 apply
no fast-carb scaling, and delta-weighted dynamic ISF inflates insulinReq
mid-rebound (3.55U SMB at BG 97, 25 min after a 67 mg/dL low; second
insulin-driven hypo followed). The V6 post-rescue cap inherited the
unrestrained dose ("capped from 4.0U to V1's 3.55U").

Fix: apply the graduated scale (0.3 below BG 120, linear to 1.0 at 170)
to the FINAL microBolus whenever the 45-min post-rescue window is active,
COB == 0 and bg < 170 — independent of tier and of the delta_accl>25
trigger, skipping cycles the in-tier scaling already handled. No velocity
escape inside the window (Fix D contamination argument). Restraint-only.

DB pricing (price_composed_guard.py, refreshed to t=now, 103k dosing
cycles, 9 users): 34% [95% CI 32-37] of removed insulin sits directly
ahead of a second low <70 (07-04 cap shipped at 27%; other levers 14-19%).
Cost: 9% genuine post-hypo meals, median 0.80U under-delivery, episodes
full dosing was not containing (median peak 240). Leave-one-out floor 27%
(drop user D). Incident replay: 9.90U -> 3.05U.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Nnt1MR3aRXXFHjxUjMSh8w
…onfirm-crash hole

2026-07-23 user-H incident (twice in one afternoon, loop then disabled by
the user): post-rescue tier block demotes T3/T5 to T7/T8, but T7/T8 apply
no fast-carb scaling, and delta-weighted dynamic ISF inflates insulinReq
mid-rebound (3.55U SMB at BG 97, 25 min after a 67 mg/dL low; second
insulin-driven hypo followed). The V6 post-rescue cap inherited the
unrestrained dose ("capped from 4.0U to V1's 3.55U").

Fix: apply the graduated scale (0.3 below BG 120, linear to 1.0 at 170)
to the FINAL microBolus whenever the 45-min post-rescue window is active,
COB == 0 and bg < 170 — independent of tier and of the delta_accl>25
trigger, skipping cycles the in-tier scaling already handled. No velocity
escape inside the window (Fix D contamination argument). Restraint-only.

DB pricing (price_composed_guard.py, refreshed to t=now, 103k dosing
cycles, 9 users): 34% [95% CI 32-37] of removed insulin sits directly
ahead of a second low <70 (07-04 cap shipped at 27%; other levers 14-19%).
Cost: 9% genuine post-hypo meals, median 0.80U under-delivery, episodes
full dosing was not containing (median peak 240). Leave-one-out floor 27%
(drop user D). Incident replay: 9.90U -> 3.05U.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Nnt1MR3aRXXFHjxUjMSh8w
(cherry picked from commit 51e7663)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Nnt1MR3aRXXFHjxUjMSh8w
(cherry picked from commit d721e9b)
Live episode 2026-07-25 ~11:00-12:40: BG parked 219-247 with IOB 1.3-2.8,
insulinReq~0 (eventualBG~target — efficacy deficit invisible to IOB), V6
dribbling 0.05-0.30U. The shadow logged trig=0/n/a throughout because of
its 200 ceiling, so the episode banked nothing. Read-only change: the
shadow now logs [145,250); the tag records BG so the two bands price
separately. ACTIVE nudge spec band unchanged at [145,200).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Nnt1MR3aRXXFHjxUjMSh8w
…miscalibration

Field calibration (2026-07-27, 5.4k cycles): pLow90 error monotone in IOB —
high-IOB tertile predicted 36% low-within-90 vs 14% realised (p>0.4 bucket
68% vs 18%), low-IOB under-predicted 9% vs 27%; QUIET_FLAT 30-min median
residual +15 mg/dL vs the ≈0 acceptance criterion. Root cause: the substrate
held the INSTANTANEOUS insulin activity constant for the whole horizon
(projBG = bg + BGI5 x h/5), overstating the fall in proportion to IOB — a
bias the regime pools cannot absorb (not IOB-conditioned).

Fix: use oref's decaying-activity IOB prediction curve (rT.predBGs.IOB, the
purple line — trustworthy per the 07-03 prediction-lines review) as the base
at h=30/60/90 in all three consumers (tracker residuals, sizer base60, pLow
base90). SERIAL_VERSION 1->2 so v1 pools cold-start and re-warm (~3-6 days).
Shadow-only: dosing untouched; bit-identity test retained. NOTE: earlier
'innov-sens frozen 70%' reading was a misread — boostV7_innovSensFrozen is
the log-only sens-frozen efficacy INNOVATION value, not a freeze flag.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Nnt1MR3aRXXFHjxUjMSh8w
…rt-grounded

30-day cohort synthesis (9 users, ~103k cycles): fully-closed regime confirmed
(92% COB=0); TIR 87±7 / TING 72±11 but 77-98 spread; 48% of lows activity-driven;
post-meal exercise 23% [20,26] vs 14% [12,17] low rate; post-exercise tail elevated
5h; highs 89% unannounced, efficacy-invisible-to-IOB (median 4.4U on board during
>180). Frontier = retractable anticipation + risk-aware sizing, not better reaction.
Public/anonymised register.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Nnt1MR3aRXXFHjxUjMSh8w
…emetry

Prompted by an untested claim in the fully-closed-loop review. Investigated
out-of-sample (GroupKFold by user, 1717 stuck-high regime entries): can anything
beyond glucose trajectory + dose-already-given predict whether on-board insulin
works (high settles) or not (stalls / rebound-crashes)?

Result: no. CRASH — GBM base 0.466->0.518 but LOGISTIC 0.453->0.500 (chance;
GBM increment was overfit). STALL — weak 0.561->0.592, carried entirely by
IOB-magnitude proxies (how much insulin, not whether it works). Loop deviation
0.474 (below chance). Recent-SMB flat 15/19/17% across tertiles. Twin inferred
carb-appearance Ra (the one dose-independent candidate) refuted: AUC 0.473,
high-Ra vs low-Ra crash 28% vs 28%, and only 12% coverage of stuck-highs.

Implications: (1) don't build an efficacy detector from current telemetry;
(2) the guards we already key on post-rescue context are not beaten by richer
features; (3) the efficacy blind spot is a SENSING problem, not modelling —
strengthens the review thesis with evidence. Review's asserted claim corrected
to a measured finding (md + artifact).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Nnt1MR3aRXXFHjxUjMSh8w
…INCONCLUSIVE, pre-register re-run

Validation caught a window artifact in the prior commit: the '12% Ra coverage /
Twin abstains' claim was wrong. Within the Twin era (since 07-18) Ra covers 88-94%
of stuck-highs and is never the null field. The real bottleneck is age — the Twin
is ~10 days old, giving only ~30 independent stuck-high episodes, so the Ra strand
is INCONCLUSIVE (not refuted). No code change helps; forcing Ra output is
unnecessary. Corrected REPORT, review md, register. Added RERUN_PROTOCOL.md
pre-registering the confirmatory Ra test (trigger: >=150 Twin-era episodes, ~1 Sep;
locked design + decision rule). Main crash negative stands SOLID and Twin-independent.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Nnt1MR3aRXXFHjxUjMSh8w
…T dose — corrects review

The review implied post-meal-exercise lows are committed-insulin-into-sensitised-body
(a dose story). Data refutes it: among 686 meal+exercise events, crashers carry LESS
insulin at exercise onset (0.96 vs 1.61U) on an identical meal bolus, and crash rate
FALLS monotonically with IOB (32/22/18% across low/mid/high tertiles). Mechanism:
exercise's largely insulin-independent glucose drain lands when the meal's carbohydrate
counterweight is thin (low IOB proxies a small/finished meal; crashers also start lower,
BG 114 vs 136). The loop is structurally on the wrong side — needs glucose-in, only has
insulin-out, already spent. Fix is anticipatory withdrawal or carbs, NOT a smaller meal
dose. Corrected review md + artifact + register. SOLID (not dose-driven); PROVISIONAL
on precise physiology; reverse-causation caveat stated.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Nnt1MR3aRXXFHjxUjMSh8w
…exposure

Separates the loop's 3 regimes (30d, 8 users w/ step feed): background 93% TIR /
82% TING (essentially solved); clean post-meal (no exercise) TAR>180 21.7% / TING
53.8 with lows FLAT at 2.5% (the loop is slow, not over-dosing); post-meal WITH
exercise trims the high (17.8%) but doubles lows (TBR70 4.0). Removing exercise-
postmeal lifts review +2.5pp TIR / +4.5pp TING. Bimodal per-user: exercise post-meal
HELPS high-runners (blunts peak, no low), HURTS tight-runners (D 9%, E 14% TBR) who
lack the carb buffer. Confirms the carb-counterweight mechanism at user resolution.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Nnt1MR3aRXXFHjxUjMSh8w
…0.67), not meals

Tests Tim's question: our prediction work used cross-cohort (GroupKFold) splits;
would per-user work better? Measured (45min-lead onset, habit features only, per-user
temporal past->future vs cross-user GroupKFold):
- EXERCISE: per-user median 0.779 (all 8 users 0.72-0.83) vs cross-user 0.672 — per-user
  decisively better; exercise timing is idiosyncratic, cross-user can't see held-out routine.
- MEAL: cross-user 0.724 vs per-user median 0.683 — mixed; meal times semi-universal so
  cross-user already strong; per-user wins only for high-data users, overfits thin-n (one
  user 0.37 on 14 events).
Implications: exercise anticipation PER-USER; meal anticipation HYBRID (cross prior +
per-user adapt, gated on event count); safety from retractability not accuracy;
non-stationarity needs online update. Scopes the reactive 'no signal' conclusion —
it doesn't bound anticipation.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Nnt1MR3aRXXFHjxUjMSh8w
…IVE, shadow-first)

Unifies the session's findings into one buildable architecture. Four components:
(A) per-user habit predictor — onset-hazard table, refit nightly offline (not in-loop
learning), exercise pure-per-user / meal hybrid; (B) retractable anticipation controller
generalising AnticipationBackoutShadow to two levers — exercise pre-REDUCE (tight-runners)
and meal MOVE-EARLIER (high-runners, strict-TBR), both retractable temp-basal never SMB;
(C) per-user population gate via auto-config, disjoint eligibility; (D) safety envelope —
absolute TBR floors, no-arm-in-post-rescue, fizzle-priced. Phasing = shadow -> per-user
price -> pre-registered within-user trial (two-test bar); exercise-reduce is first live
lever (insulin-removing, safe-signed). Nothing doses.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Nnt1MR3aRXXFHjxUjMSh8w
…V6 and V7

Builds the anticipation architecture spec's shadow stack (read-only, doses nothing),
in the shared engine host (OpenAPSBoostPlugin.runEngine) so it runs identically for
plain Boost, V5/V6, and the V7-shadow line.

- AnticipationHabitModel: per-user onset-hazard table (hour-of-week + recency), circular-
  smoothed, per-slot confidence-blended with a cross-user prior; refit offline (not in-loop
  learning). Exercise pure-per-user; meal hybrid via a breakfast/lunch/dinner prior.
- AnticipationOnsetStore: persisted rolling exercise/meal onset history (StringKey JSON,
  V7 idiom, ~56d window), tolerant deserialize.
- RetractableArm: generic arm→confirm/back-out state machine (retractability = the safety
  mechanism); confirm = event appears, veto = post-rescue window.
- AnticipationShadow: orchestrator — onset edge-detect, ~6h refit, predict p(onset) @45min,
  run both arms, emit anticip=... tag. Never throws.
- StringKey ApsBoostAnticipHistory; extractor anticip= parser + 14 DB columns.
- 11 pure unit tests green (habit model, store, arm, orchestrator).

Phase 3 (per-user pricing) + Phase 4 (within-user trial) remain per the two-test bar;
population gating + live action are logged-not-enforced.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Nnt1MR3aRXXFHjxUjMSh8w
…V6 and V7

Builds the anticipation architecture spec's shadow stack (read-only, doses nothing),
in the shared engine host (OpenAPSBoostPlugin.runEngine) so it runs identically for
plain Boost, V5/V6, and the V7-shadow line.

- AnticipationHabitModel: per-user onset-hazard table (hour-of-week + recency), circular-
  smoothed, per-slot confidence-blended with a cross-user prior; refit offline (not in-loop
  learning). Exercise pure-per-user; meal hybrid via a breakfast/lunch/dinner prior.
- AnticipationOnsetStore: persisted rolling exercise/meal onset history (StringKey JSON,
  V7 idiom, ~56d window), tolerant deserialize.
- RetractableArm: generic arm→confirm/back-out state machine (retractability = the safety
  mechanism); confirm = event appears, veto = post-rescue window.
- AnticipationShadow: orchestrator — onset edge-detect, ~6h refit, predict p(onset) @45min,
  run both arms, emit anticip=... tag. Never throws.
- StringKey ApsBoostAnticipHistory; extractor anticip= parser + 14 DB columns.
- 11 pure unit tests green (habit model, store, arm, orchestrator).

Phase 3 (per-user pricing) + Phase 4 (within-user trial) remain per the two-test bar;
population gating + live action are logged-not-enforced.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Nnt1MR3aRXXFHjxUjMSh8w
(cherry picked from commit 5ea788a)
(cherry picked from commit 43f8266)
… figures

Synthesis of the segmented-performance + carb-counterweight-mechanism studies:
meal+exercise low 23% [19,27] vs 15% [12,18] no-exercise (CIs disjoint); crash NOT
dose-driven (falls with IOB 32/20/17%, crashers carry LESS insulin on identical bolus)
= carbohydrate-counterweight mechanism; segmented zones (background solved 93% TIR;
post-meal high is speed-not-dose, lows flat); two disjoint per-user populations
(high-runners → more meal insulin, tight-runners → exercise protection). 4 matplotlib
figures (Okabe-Ito CVD-safe), make_figures.py reproducible. On Boost-V7-shadow.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Nnt1MR3aRXXFHjxUjMSh8w
…ly-2026 changes

The four dev-facing docs/tools were ~49-72 commits behind. Brought current:
- Composed post-rescue REBOUND GUARD (scales SMB in the 45-min post-low window,
  closes the confirm-crash) — README §3, tuning-guide safety layer, simulator
  v6Override (now modelled, only ever removes insulin).
- HR-exercise INACTIVE safety fix (stop adding insulin during elevated-HR exercise)
  — README §3/§8, tuning-guide safety layer, simulator activity-state note.
- Aggression ceiling 1.3 → 1.6 — README §4/§5, tuning-guide spectrum + scenarios,
  simulator slider max.
- A2 confirm-sooner + C5 velocity-budget-floor opt-ins, and the auto-config-managed-
  switch convention (insulin-adding levers gated to well-controlled) — README §4/§5,
  tuning guide.
- Per-user ANTICIPATION shadow (read-only, learns routine, doses nothing) — README
  §3, tuning guide, analyser note.
- Corrected stale README §8 exercise-into-correction claim; neutralised a NS-host
  placeholder. All three tools' JS syntax-checked.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Nnt1MR3aRXXFHjxUjMSh8w
(cherry picked from commit 5c230e0)
…nt the 3 levers; fix accuracy

Reworked the monolithic README per new-user feedback:
- Slim README = what it is, one-glance how-it-works, GETTING STARTED SAFELY (shadow-first
  1-2-3), and THE THREE LEVERS as a clear table (what each is + how up/down changes dosing),
  with everything else flagged advanced-and-auto-set. Links out to the tools + detail pages.
- Split the deep sections into separate pages: docs/v6-how-it-works.md, v6-advanced-settings.md
  (the 'set for you on install' page — auto-config rules + every advanced setting),
  v6-heart-rate-and-sleep.md, v6-safety-and-validation.md.
- Tuning guide now cross-links to the README levers + the advanced-settings reference where
  settings are discussed.

Accuracy fixes (verified against code via review):
- HR-exercise guard: 'stops adding insulin' -> 'suppresses the inactivity profile-raise and
  lifts target to ~160' (it is the inactivity branch, not a global stop).
- Auto-config insulin-adding switches gated by the STRICT well-controlled cut (TBR70<1.5% AND
  <54<0.3%), not 'consensus targets'; composed-brake floor correctly described as a live
  runtime gate (TBR63<2.0% AND <70<3.5%), NOT auto-config-managed.
- 'one-shot flag' -> per-knob resolution; raise-guard also names the severe TBR54>=1% co-guard.
- Aggression budget = per-cycle sizing base (30% floor), not a burst ceiling; non-meal cap
  notes the velocity-budget path; minGuardBG = configured LGS threshold (default 80).
- A2/C5 shorthand replaced with real toggle names (Aggressive early confirm / Velocity budget).
- Version naming reconciled (V6 label vs V5 code/keys). 0 broken links; JS + PII checked.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Nnt1MR3aRXXFHjxUjMSh8w
(cherry picked from commit 4ccf256)
…rnal A2/C5 codes from user docs

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Nnt1MR3aRXXFHjxUjMSh8w
tim2000s and others added 22 commits September 1, 2026 15:08
dev has carried the primer as released on 20-21 July. The four corrections made
on 30 July have sat on the shadow line since, and they fix defects in a feature
that doses.

The trigger was the material one. deltaAccl is a percentage whose denominator
floors at 2.0, so on a flat trace the shipped gate reduces to a rise of 0.2
mg/dL, a fifth of one CGM quantisation step. Measured over 90 days and 25,766
points, that gate was the worst of ten candidate detectors, giving 43.8 per cent
probability of a real rise against a 28.9 per cent chance rate while firing on
26.7 per cent of all cycles. The magnitude scaling ran backwards, a flat trace
scoring 33.5 where a genuine 11 mg/dL per five minutes scored 12.5, and it sat
saturated at its ceiling on five of six live fires. Twelve of twenty live fires
came at a delta of 2 mg/dL or less, and one delivered 1.35 U into a flat 120 that
reached a nadir of 68 and could not be recovered by 65 minutes of zero temp.

The remaining three set the ceiling to scale with the user's own committed cap
rather than a flat constant, and settle the routing question for hypo-prone
users. The lock added in the third is removed by the fourth: auto-config may
recommend a route but must not make a setting unreachable, so the blocking
behaviour is replaced by visibility, and an overridden routing is now greppable
as primerRoute=bolus-USER-OVERRIDE(recommended=tbr).

Taken in order so the pair that cancel do so as they did on the shadow line. The
auto-config periodic re-derivation of 3-4 August is deliberately not included: it
is a separate feature, it rewrites BoostV5AutoConfigApply heavily, and the primer
commits predate it, which is why they applied here cleanly.

705 tests pass in :plugins:aps, including PrimerTest and BoostV5AutoConfigTest.
The Dagger graph validates via :app:kspFullDebugKotlin.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016VyQsqdmejUkeKhSJoxZmH
The README rewrite that shortened this file from 354 lines to 136 dropped the only
description of the Boost Bolus Cap and of the UAM basal-minutes limit, and the
promotion of dev to master carried that loss onto the released line. Both settings
are live: auto-config resolves ApsBoostBolus from the user's own dosing history,
and ApsUamMaxMinutesOfBasalToLimitSmb is what DetermineBasalBoost multiplies by the
current basal to arrive at maxBolus.

Restored into the levers section rather than the advanced page, since a dose coming
out smaller than expected is usually one of these two and the reader should not have
to leave the README to find out which.

Figures taken from the keys rather than from the old text, which predated the
default moving to 15 minutes and still said 20. Also states that the cap is a size
and not a rate, because it limits one bolus and says nothing about how often that
bolus repeats, which is the property that made a one-minute loop deliver a third
more insulin than a five-minute one on the same settings.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016VyQsqdmejUkeKhSJoxZmH
Brings everything running on the V7-shadow line that dev did not have: the confirm
tranche behind its toggle, the one-minute cadence path, auto-config re-derivation
on a seven-day cadence, the VWA TDD and consequence-prior shadows, the
install-time history backfill, and two safety fixes carrying no new behaviour, the
DynISF implausible-TDD guard and the ML ring buffer expiring by age.

Conflicts resolved in favour of the shadow line, which is the vehicle branch and a
superset in every case checked. The post-rescue rebound guard survives and gains
the tight-ramp trial arm. The engine collaborator moves from Provider to Lazy,
which is the correction from the incident where an unscoped Provider handed the
writer and the reader different instances and the loop enacted nothing for five
days.

One exception is restored separately: dev carries .setDefault() making Boost V6
the default APS engine, which the shadow line does not have, and that must not be
lost.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016VyQsqdmejUkeKhSJoxZmH
…icated

The shadow merge took the union of both sides where the same key had been added
independently, so StringKey ended with two identical ApsBoostAnticipHistory
entries and core:keys would not compile. Enum entries cannot be declared twice.

Caught by the build rather than by review, which is the argument for compiling
after any merge resolved with a strategy option: -X theirs resolves hunk by hunk
and will happily produce a file that no side ever contained.

799 tests pass in :plugins:aps and the Dagger graph validates once this is in.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016VyQsqdmejUkeKhSJoxZmH
The draft was organised in the order the analysis happened rather than the order
anyone reads in, opened with a 450-word abstract that was mostly areas under the
curve, and never once showed what any of it looks like at a single rise. It passed
every mechanical style check and was still hard to follow, which is the point: the
fault was structure, not typography.

Rebuilt as a sequence of questions in the order a controller faces them. Can it
tell food arrived, can it tell how much, and since it can do the first and not the
second, what should it ask instead. A worked case comes second, one rise beginning
at 138 mg/dL at breakfast, carrying the three answers in plain terms before any of
them is defended.

Statistics moved behind the claims rather than in front of them. Detection now
leads with the operating point, 0.35 false alarms against 0.39 true detections per
day, because that is what a person can act on and an area under the curve is not.
Size leads with the fact that predicting a person's own median meal beats a model
given the trajectory, the clock and their whole announcement history, which needs
no statistics at all to land.

2,384 words against 3,042, 31 three-decimal figures against 48, and a 192-word
summary against 450. Method and corpus sizes moved to their own section near the
end, where they support the argument instead of delaying it.

Nothing measured has changed and no figure has moved.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016VyQsqdmejUkeKhSJoxZmH
The structure was right after the last pass and the prose was still academic. Mean
sentence ran 20.5 words with sixteen over thirty, and the draft carried five
negation-then-correction constructions and five asides about its own argument, both
of which are on the list of things that make writing read as machine-generated.

Now 1,816 words at a mean of 12.2 per sentence, one over thirty and none over
forty. No "this is not X, it is Y" anywhere. No sentences explaining what the
document is about to do.

The other change is concreteness. Abstract nouns that stood in for the actual thing
have gone: "that judgement", "the trade", "what remains", "the addition". Numbers
that were carried in prose have moved into the two tables, so paragraphs argue and
tables count.

Nothing measured has changed and no figure has moved.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016VyQsqdmejUkeKhSJoxZmH
…reader

The previous draft was aimed at somebody building a loop and had drifted into a
developer's register, with asides and conversational fragments that will not carry
to a reviewer who has no stake in the codebase. This is the same work written for
an independent medical or technical reader assessing what is known about automating
mealtime management.

Structure is conventional: structured abstract, background, methods with the data
and the definitions stated before any result, results, discussion, limitations,
reproducibility. Provenance is now explicit throughout, including that the corpora
are public research datasets and that the participants are not users of this fork,
which is the first thing such a reader will want to establish.

Units are mmol/L throughout, since the audience works in them. Area under the curve
is defined at first use. The detection section states the class-construction
asymmetry and reports the matched figure, with the inflated 0.952 named as the one
not to quote, because a reviewer will otherwise find it themselves and wonder.

The clinical framing is carbohydrate announcement as the remaining demand on the
person, and the finding stated against it: the fact of a meal is recoverable, the
quantity is not, and a system therefore cannot substitute an inferred entry for a
counted one. The discussion adds the point a reader of the literature needs, that
reported meal-size estimates should be judged against a comparator carrying
participant identity and time of day, since in these data that comparator accounts
for essentially all of the apparent performance.

2,536 words. Nothing measured has changed and no figure has moved.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016VyQsqdmejUkeKhSJoxZmH
The draft called them meals and never described them, which invites the obvious
question: a median of 28 g is small for a meal. It is not a meal. It is a
carbohydrate entry of at least 8 g, not made at or below 4.4 mmol/L, with nearby
entries merged, occurring 2.1 times per participant-day with an interquartile range
of 16 to 44 g.

The number that matters more is the daily total. Participants announced a median of
63 g per day and 92 per cent announced under 100 g, well below likely intake. So
much of what these participants ate was never announced.

Two consequences are now stated where they belong rather than left for a reader to
work out. The undeclared class used as the detection comparator contains real
unannounced meals, which is why the false-alarm figures are conservative. And the
size analysis concerns announced meals, which may differ systematically from all
meals.

Measured from the extraction the paper actually used, not from the raw carbohydrate
table, which gives a different median because it applies neither the rescue
exclusion nor the merge.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016VyQsqdmejUkeKhSJoxZmH
A script, a report and one figure placing all 564,201 announcements from 1,028
participants on a single 24-hour axis, which is the descriptive substrate the
meal-size paper rests on and never showed.

Timing has strong structure and size has almost none. Announcements peak at 11:00,
12:00 and 18:00, with the six hours from midnight carrying 5 per cent of them. The
median size runs between 20 and 33 g across the entire day, so knowing the hour
tells you when somebody eats and close to nothing about how much.

Individuals are much less regular than the pooled picture suggests. The median
participant announces 29 per cent of their carbohydrate inside their own three
busiest hours, on a range of 0.18 to 0.67 across 996 participants with enough data.
The structure in the figure is therefore population-level, which is consistent with
the per-user anticipation shadow having failed to beat a plain hour-of-day rate.

Two things the figure exposes that are worth having on the record. 72 per cent of
announcements are a multiple of 5 g and the six commonest values account for 46 per
cent of everything entered, so the target any size model is scored against has a
resolution of about 5 g before its accuracy is even considered. And announced
carbohydrate totals a median of 63 g per participant-day, well under likely intake.

A true scatter is the wrong mark at this count and would produce a filled
rectangle, so the main panel bins to hexagons on a log count scale. --sample draws
raw points over it as a check on the binning rather than as the deliverable.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016VyQsqdmejUkeKhSJoxZmH
…ts centre

A median that barely moves could still hide an hour whose spread is quite
different, so the report now carries the full quantile table by hour, 10th through
90th with the mean and the count, and a second figure draws it.

It does not hide anything. The interquartile range is 10 to 20 g wide in every hour
of the day. The 90th centile moves by about 20 g between the quietest hour and the
busiest, and the 10th centile sits at 10 to 14 g throughout. The hour shifts the
distribution a little and reshapes it hardly at all, which is the same conclusion
the modelling reached and is now visible without a model.

Twenty-four box plots would add ink without adding information when the
distributions are this alike, so the figure uses nested quantile bands and lets the
likeness be the visible result.

Three layout faults found by looking at the render rather than trusting it. The
legend sat on the data at the 11:00 and 18:00 peaks where the 90th-centile band
reaches the top of the plot, so it moved outside the axes and the title gained the
padding to make room. The all-day median reference had no clear space anywhere at
its own height, so it is named in the legend instead of annotated on the plot.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016VyQsqdmejUkeKhSJoxZmH
… entry

Read straight from the studies schema rather than from the meal-readability
extraction, so this is all 1,617,910 entries anybody made, before the 8 g floor,
the rescue exclusion and the merging of nearby entries.

It is not a bell curve and neither of the obvious fits describes it. Skew is +1.70
and excess kurtosis +5.38 against 0 and 0 for a normal. A normal of the same mean
and standard deviation puts its peak at 29 g where the data peaks near 15 to 20,
is symmetric where the data has a long upper tail, and places mass below zero. A
log-normal, the usual first guess for a positive right-skewed quantity, peaks far
too early at about 10 g, undershoots everything between 15 and 35, then overshoots
the tail. On a log scale the skew is -1.07, so the data is not log-normal either.

Two panels because the distribution has two separate features and one hides the
other. At one-gram resolution it is a comb: 66 per cent of entries are a multiple
of five and 38 per cent a multiple of ten, which is people estimating and then
rounding. At five-gram resolution the comb disappears and the underlying shape is
visible.

The rounding matters beyond presentation. Any model asked to predict carbohydrate
is scored against a target whose own resolution is about 5 g before its accuracy
against actual intake is considered at all.

Only two of the seven corpora record carbohydrate, so the title names them rather
than implying the whole dataset.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016VyQsqdmejUkeKhSJoxZmH
Breakfast 06:00-09:00, lunch 12:00-15:00, dinner 17:00-20:00, over all 1.6 million
carbohydrate entries in the two corpora that record them.

The three distributions are close to the same curve. The median is 25 g in all
three windows, identical rather than merely similar, against an interquartile range
of 24 to 30 g inside each. Knowing which meal it is tells you nothing about the
middle of the distribution.

What does differ is the upper tail. The 75th centile runs 39 g at breakfast against
45 g at dinner and the means follow, 28, 30 and 32 g. The later windows are not
made of larger meals so much as the same meals plus a heavier tail of large ones,
which is a different thing and matters if a controller were ever to size to the
window.

The curves are a one-gram histogram smoothed with a Gaussian kernel of 3 g rather
than a kernel density estimate over the raw values. Cheaper on 1.6 million points
with bounded integer support, and the smoothing is doing a stated job: unsmoothed,
each curve is a comb, because two thirds of announcements are a multiple of five.

Palette validated rather than assumed: the first three categorical slots pass every
gate all-pairs on this surface, with the aqua carrying a contrast warning, so every
curve is direct-labelled and the numbers appear as a table.

Two defects the render caught. All three curves peak within a few grams of each
other, so labelling at the mode stacked three labels on top of each other and on
the title; they now sit at the point of maximum separation between the curves,
staggered by rank so the closest two cannot touch. And the first summary sentence
named the same window as both largest and smallest, because with three identical
medians max and min both return the first key.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016VyQsqdmejUkeKhSJoxZmH
… axis as a percentage

Splitting them overturns what the pooled figure said. Loop contributes 90 per cent
of the carbohydrate entries, so the pooled distribution was Loop's with a little
ReplaceBG mixed in, and the finding that the three meal windows share an identical
25 g median was a property of one corpus reported as a property of both.

Loop is flat across the day, medians 24, 23 and 25 g, and peaks near 15 g.
ReplaceBG rises steadily, 32, 35 and 40 g, and peaks near 30. So whether meal
windows carry size information depends on which population is being described.

The likely reason is what the two groups were doing rather than what they ate. Loop
participants ran a closed loop and entered carbohydrate about twice as often per
person in smaller amounts, which is the pattern of announcing snacks and
corrections alongside meals; ReplaceBG participants on sensor-augmented pump
therapy a decade earlier appear to have announced meals. The report marks that as
an interpretation rather than a measurement.

Two panels rather than six curves on one axis, because with six the studies would
be separable only by line style and the comparison a reader wants, window against
window, becomes the harder of the two. Panels share a vertical scale so the
difference in shape between corpora is visible and not an artefact of scaling.

Vertical axis is now a percentage of that window's announcements.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016VyQsqdmejUkeKhSJoxZmH
…mmons

I said the Commons was not in the database. It is, and I had looked badly: I
checked oref_v5, found 29 participants tagged U000 to U028, and generalised from
one table. oref_v6 holds U029 to U072 and oref_v7 holds U073 to U182, the three
partitioning 183 participants with no overlap. The platform column labels v6 as
AndroidAPS pre-DynISF and v7 as OpenAPS oref0 SMB; v5 carries no such column and
is labelled by its table name rather than guessed at.

It is there as loop decision records rather than treatments, which is why a scan
for carbohydrate columns missed it: the tables carry carbohydrate on board and no
carbohydrate entries. Announcements are therefore derived from step increases in
COB of 8 g or more between adjacent cycles, which measures the amount entered less
whatever decayed inside the five-minute cycle. Sizes are biased slightly low,
entries made inside one cycle merge, and this is the loop's belief after its own
absorption model rather than what somebody typed. It is a different measurement
from the Jaeb entries and the two should not be compared to the gram.

126,064 announcements, 54,323 of them inside the three windows.

Medians by window: OpenAPS 30, 29, 30 g, flat like Loop. AndroidAPS 39, 28, 32 g
and the unlabelled table 30, 25, 32 g, both with a wider span, though on a tenth of
Loop's sample and visibly noisier for it.

A defect worth recording since it produced a plausible-looking wrong answer twice:
the COB column is quoted uppercase in one of these tables and lowercase in the
others, so a hardcoded name fails on one table and a case-insensitive assumption
would silently read the wrong thing. It is now resolved per table from
information_schema.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016VyQsqdmejUkeKhSJoxZmH
…participant

Asked whether people enter the same amount more than once inside an hour in the
meal windows. They do, a little, and the answer only survives a control.

6.4 per cent of entries in the windows have a same-size partner within the hour.
Shuffling each participant's own sizes across their own timestamps, which
preserves both their value distribution and their timing, gives 3.7 per cent. So
the excess is 2.7 percentage points, about 1.7 times chance. Without that control
the raw rate reads as a strong pattern and is mostly coincidence, because six
amounts account for nearly half of all entries.

Per participant it is widespread and slight: 77 per cent sit above their own
chance rate, the median excess is 1.5 percentage points, and 14 per cent exceed
five. A minority do it a great deal.

The shape of the repeats is the useful part. A quarter of the pairs are under five
minutes apart, the median gap is 16 minutes, and the amounts that repeat are small,
15, 10 and 20 g against an overall median entry of 28. Small amounts at short
intervals reads as one meal entered in parts rather than somebody eating the same
thing twice, which is a reason to treat the small end of the size distribution as
partly an artefact of entry behaviour.

A defect worth recording because it produced a confident wrong answer first. The
initial run reported 94 per cent, against a shuffled control of 94 per cent, and
the reason was that ts.astype("int64") / 1e9 returns microseconds on this pandas,
so the one-hour window was a thousand hours and every entry matched something. That
is the same conversion fault recorded this morning against the treatment table and
repeated here. The conversion is now unit-agnostic and asserted, and the run
lengths that exposed it, a mean of 676 chained entries where the real median gap is
330 minutes, are the check that should have been run first.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016VyQsqdmejUkeKhSJoxZmH
…hat a cluster totals

Repeating a size turns out to be the special case. Of successive entries inside
half an hour, 87 per cent differ in size. The same-size share does climb as the gap
shortens, from 9.1 per cent beyond an hour to 22 under five minutes, which is the
repeat effect measured in the previous commit, but it never becomes the majority.
The second entry is usually smaller than the first, 59 per cent against 41, with a
median absolute difference of 13 g.

The consequence is in the clustering. Chaining entries within half an hour and
treating each chain as one eating occasion, 87 per cent of clusters are a single
entry, 11 per cent are two and 2 per cent are three or more, and 25 per cent of all
entries belong to a multi-entry cluster. Those clusters total a median of 53 g
against 28 g for a lone entry, and the pattern holds across thresholds: 50 g at
fifteen minutes and 56 g at sixty, absorbing 16 and 36 per cent of entries
respectively.

So for a quarter of entries the eating occasion is roughly twice the size the entry
records. Every statement in this programme about how large a meal is depends on
whether split entries were merged first, and the answer moves by about a factor of
two. The readability extraction does merge; the raw studies table does not, which
is why its median reads 25 g and the extraction's reads 28.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016VyQsqdmejUkeKhSJoxZmH
The readability extraction keeps the first entry of a cluster and skips anything
within ninety minutes, recording that first entry's grams as the meal. It does not
merge, which is what the paper's methods section claims. Against the raw table that
discards 26 per cent of entries and 22 per cent of all carbohydrate, and for the 28
per cent of meals that lose something the recorded size is a median 25 g against an
actual 55 g occasion.

That is a defect in the target rather than in the model, so it was tested rather
than argued about. extract_meals_merged.py rebuilds the extraction with one change,
adding those entries to the meal instead of dropping them, importing every filter,
the onset rule and the features from the original so nothing else can move. Meal
boundaries are identical and both extractions produce exactly 492,440 Loop meals;
only the grams differ. The median goes from 28 g to 34.

It does not rescue the finding. Over the clock the trace adds +0.006 at ten minutes
against +0.008 before. Over person, clock and history it rises from +0.021 to
+0.032, the direction a less noisy target should move it, and still well under the
pre-registered 0.05. On quantity the model matches each participant's own median on
both targets and beats it on neither, 17.0 g against 17.0 on the merged target as
it was 13.1 against 13.0 before.

The reason the original held is not target noise. Forty grams of difference moves
the ten-minute rise by under 1 mg/dL against a between-meal spread of about ten,
and merging changes the target without touching that ratio.

The correction still matters for description. The size distribution quoted
throughout this work was a distribution of first entries, and the paper says merged
where the code discards. Fixing that text is the next commit.

Merged parquets are gitignored; they are regenerated by the script in 28 seconds.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016VyQsqdmejUkeKhSJoxZmH
The methods section said entries close in time were merged. The code does the
opposite: it keeps the first entry of a cluster and does not count the rest, so a
meal entered in parts is recorded at the size of its first part. I wrote that
sentence from the entries-per-day rate rather than from the code, and it was wrong.

The section now states the rule and its cost, that it sets aside 26 per cent of
entries and 22 per cent of all carbohydrate, and that for the 28 per cent of meals
losing something the recorded size is a median 25 g against 55 for the occasion it
belongs to.

It also reports the re-run, since a reader is entitled to ask whether the finding
survives its own correction. It does. The trace adds 0.006 at ten minutes over the
clock rather than 0.008, and 0.032 rather than 0.021 over the fuller baseline, both
short of the 0.05 margin, and the model still matches each participant's own median
on quantity. The limitations now say plainly that every size in the paper is a first
entry rather than an eating occasion.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016VyQsqdmejUkeKhSJoxZmH
FINDINGS.md draws the six scripts in this folder together. Every figure in it is
produced by one of them and regenerates by running it.

The short version is that timing carries real structure and size carries almost
none, and that what looks like size is mostly the identity of the person and the
hour. Neither changed when the meal-counting defect was corrected.

The more useful result is about the data rather than the model. These corpora do
not record a sequence of meals, they record a sequence of entries, and the gap is
large enough to move the median meal by about a fifth. A quarter of entries belong
to an eating occasion roughly twice their own size, 72 per cent of announcements
are a multiple of 5 g, and announced carbohydrate totals a median of 65 g per
participant-day against a likely intake well above that.

meal_clock.py carried the same wrong sentence the paper did, that entries close in
time were merged, and it is corrected there too: the extraction keeps the first and
does not count the rest.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016VyQsqdmejUkeKhSJoxZmH
…d what the corpora buy

The ML feature work and two reports. The timing work lives in the meal-investigations
repository and is committed there.

WHAT THE CORPORA BUY (backtesting/scripts/2026-09-ml-features/). Four candidates
priced, held out by participant, against matched baselines carrying glucose at the
anchor plus the clock, with paired participant-bootstrap intervals.

One clears the bar and it is not a new model. A hypo-risk model trained on the 183
OpenAPS Data Commons participants, applied unchanged to Boost's own users, beats a
Boost-trained equivalent by +0.024 AUC (+0.013 to +0.034) at one hour and +0.073
(+0.026 to +0.102) at four, and beats the matched baseline by +0.054 at both. The
learning curve behind it runs 0.683 at 7 training participants, 0.729 at 28, 0.745 at
112, and nothing further at 146. The shipped model saw 28, which sits on the steep
part. This is a training-set effect needing no new feature and no change to the dose
path. PROVISIONAL: the comparison arm was a 16-user refit rather than the production
artefact, which a shadow log would settle.

Two well-evidenced nulls. The fall-consequence prior, the mirror of the shipped rise
prior, reduces to a near-monotone function of glucose at the onset: the shape adds
+0.017 to +0.042 and never reaches the 0.05 margin, and the clock adds nothing at all
on falls against roughly +0.04 on rises. Conditioning the post-rescue guard on a
repeat-low score works on the Commons (+0.065) and the corpora (+0.054) and fails on
Boost's own users (+0.029 to an absolute 0.529, with the Commons estimate outside the
Boost interval); no decile beats the fixed rule.

The general lesson, worth the register entry more than any candidate: where a target
can be defined on corpus data, Boost's own cohort is the wrong place to fit it,
because 16 to 28 participants is the steep part of the curve and external transfer
costs nothing measurable.

LGBM METHODS PAPER (backtesting/reports/2026-09_boost_lgbm_methods.md). Both models
are LightGBM binary classifiers exported as JSON and walked by about fifty lines of
Kotlin. It records what the code does rather than what the documentation says, and
three things follow. The meal model is dose-increasing on three paths, releasing the
V1 tier hold above 0.50, carrying weight 0.20 in the V5 meal-signal score, and
blocking the sleeping classification above 0.30; that is verified in the shipping
source and is not currently written down anywhere. Hyperparameters were never searched
for either shipped model. And the field audit predates the August ring-buffer fix, so
current calibration is unmeasured.

EXERCISE PRE-MEAL RESEARCH (backtesting/reports/2026-09_exercise_premeal_research.md).
EXTOD, Riddell 2017 and the 2025 EASD-ISPAD AID statement, with their numbers set
against each other and mapped to the Boost mechanisms that could carry them. Ordering
is the load-bearing part: raise the target one to two hours ahead, then cut the bolus,
or the loop replaces the insulin. Two gaps stand in the way of implementing any of it:
COB is absent from V5Inputs, and a declaration carries no start time, type or
intensity, which are the four things the protocol levers key on.

The 491 MB extraction cache is gitignored and rebuilds from the database.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016VyQsqdmejUkeKhSJoxZmH
…ise claim

Two corrections, both to text that a reader would otherwise act on.

CLAUDE.md and the register said the deterministic engine runs "two pre-trained ML
models at inference" and left what those models do to a dose unstated, next to a
prominent note elsewhere that the hypo model is hypo-directional. That asymmetry
invites the reading that neither model can add insulin, which is wrong.
`mlMealLikely` is dose-increasing on three paths, verified in the shipping source:
it releases the V1 G3 hold that gates the aggressive tiers above 0.50, carries
weight 0.20 in the V5 meal-signal score whose states multiply the dose 0.3 to 1.8
times, and blocks the sleeping classification above 0.30. A learned component does
add insulin in the shipping engine and that is now written down.

2026-07_meal_with_and_without_exercise.md still carried the "crashers carry less
insulin" claim at full confidence, five weeks after it was withdrawn on 2026-08-13
as a pooled-absolute-units artefact. The section now opens with the withdrawal, the
corrected figure (AUC 0.549, 0.512 to 0.604, standardised by own TDD) and a pointer
to the recheck. The report keeps its own house style of reading as a current draft;
this is the exception, because somebody reading the old claim would draw the
opposite conclusion about dosing.

The register gains five entries from today's investigations: the model
directionality above, the hypo model's training-set constraint, and three results
that close questions rather than open them, the fall-consequence prior, the
post-rescue guard conditioning, and the clock as a detection feature.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016VyQsqdmejUkeKhSJoxZmH
Bumps androidx.wear.protolayout:protolayout from 1.3.0 to 1.4.2.

---
updated-dependencies:
- dependency-name: androidx.wear.protolayout:protolayout
  dependency-version: 1.4.2
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
@dependabot dependabot Bot added dependencies Pull requests that update a dependency file java Pull requests that update java code labels Sep 3, 2026
tim2000s added a commit that referenced this pull request Oct 8, 2026
Patch number on the released line for fixes that only withhold or bound
insulin, except where noted in the individual commits: tranche release
bounded by the caps and gates (#1), pre-meal target off at night, while
asleep, under a temp target and after a low (#8), V6 error falls back to
base oref (#9), primer respects running low temps and V6's hard gates
(#10), missing heart rate blocks the INACTIVE raise (#12), the sleep
boundary exit no longer reopens INACTIVE or the gate for a cycle (#18),
percent-scale divisor no longer shrinks above 180 (#4), SMB clamped to
max_iob (#5), G3 hold kept after a recent low (#19), spike override
limited to Tier 8 outside post-rescue and ML downgrade (#20), one confirm
per meal session (#7), stale meal state reset (#17), and exercise
classification restored inside the night window (#11, a regression from
the overnight gate fix).

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014RS6G2gfvdzoJAx9wPCeA4
tim2000s added a commit that referenced this pull request Oct 8, 2026
Patch number on the released line for fixes that only withhold or bound
insulin, except where noted in the individual commits: tranche release
bounded by the caps and gates (#1), pre-meal target off at night, while
asleep, under a temp target and after a low (#8), V6 error falls back to
base oref (#9), primer respects running low temps and V6's hard gates
(#10), missing heart rate blocks the INACTIVE raise (#12), the sleep
boundary exit no longer reopens INACTIVE or the gate for a cycle (#18),
percent-scale divisor no longer shrinks above 180 (#4), SMB clamped to
max_iob (#5), G3 hold kept after a recent low (#19), spike override
limited to Tier 8 outside post-rescue and ML downgrade (#20), one confirm
per meal session (#7), stale meal state reset (#17), and exercise
classification restored inside the night window (#11, a regression from
the overnight gate fix).

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014RS6G2gfvdzoJAx9wPCeA4
(cherry picked from commit a3cc1af)
tim2000s added a commit that referenced this pull request Oct 8, 2026
Patch number on the released line for fixes that only withhold or bound
insulin, except where noted in the individual commits: tranche release
bounded by the caps and gates (#1), pre-meal target off at night, while
asleep, under a temp target and after a low (#8), V6 error falls back to
base oref (#9), primer respects running low temps and V6's hard gates
(#10), missing heart rate blocks the INACTIVE raise (#12), the sleep
boundary exit no longer reopens INACTIVE or the gate for a cycle (#18),
percent-scale divisor no longer shrinks above 180 (#4), SMB clamped to
max_iob (#5), G3 hold kept after a recent low (#19), spike override
limited to Tier 8 outside post-rescue and ML downgrade (#20), one confirm
per meal session (#7), stale meal state reset (#17), and exercise
classification restored inside the night window (#11, a regression from
the overnight gate fix).

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014RS6G2gfvdzoJAx9wPCeA4
(cherry picked from commit a3cc1af)

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

dependencies Pull requests that update a dependency file java Pull requests that update java code

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant