b54ccccd0a
SavePresenceState had no caller outside tests. GatherState computed the score, resolved the bucket against the last one and threw the result away, so the singleton row was never written at all. Two things were broken by the one missing write. Hysteresis was dead. lastBucket read the cold-start Away every tick, so store.Resolve only ever took the `last == Away` arm and demanded a full PresenceEnter score to say he is at the desk. The 0.30-0.55 hold band the function exists to provide never applied once — with a 60s desk poster and tau=8min, presence dropped at about four minutes of idle instead of holding to the exit threshold at about nine. And every readout lied. /dash and ipc.Presence read this row, so they showed "away — score 0.00 (never)" while desk_active facts arrived every sixty seconds from workpc. The write goes in the tick, not in GatherState: that method holds a read-only transaction on purpose, one consistent snapshot per tick, and a write inside it would either break that guarantee or quietly upgrade the transaction. A failure logs and the tick continues, because the gate reads the in-memory bucket — which is why nudge routing kept working through all of this, and why the defect lived long enough to be found by looking at a dashboard. The existing hysteresis test scores the pure function and passed throughout, which is why nobody caught it. The new tests assert the round trip instead: the tick writes what gather resolved, a later write overwrites rather than appends, and the persisted bucket is what makes the hold band apply. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011x5DgnExQ5XZy8TZPs5bot