Skip to content

presence: arrival detection flaps — at_home has no hysteresis, so one arrival greets six times #305

Description

@adrianwedd

Split out of #304, where it was parked as a secondary finding.

What

person_arrived_home:adrian fired 6× in ~100 min on 2026-08-26
(logs/px-mind.log:19947-20029). Each transition is a real dispatch opportunity:
greet_arrival is deliberately exempt from the 30-minute expression cooldown
and from the absence gates (mind.py:478-480) — arrivals are the one moment
SPARK is allowed to interrupt — so a flapping tracker turns that exemption into
repeated unsolicited speech for one arrival.

Mechanism

mind._enrich_tracker_entry (mind.py:909-937) derives at_home as a bare
threshold with no hysteresis:

"at_home": place == "at-dads",          # semantic-address branch
"at_home": distance_km < 0.15,          # coordinate branch

_detect_findmyhub_arrivals (mind.py:970-993) then fires on any
not prev.at_home and curr.at_home edge. Chipolo GPS accuracy on the live
ha_presence sample is gps_accuracy_m: 34.0, and the house sits near the
150 m radius boundary for at least one tracker, so ordinary jitter crosses it
repeatedly.

GREET_ARRIVAL_COOLDOWN_S = 120 (spark_config.py:16) is the only damper and
it is far shorter than the observed ~17-minute flap interval, so it does not
absorb this.

Wanted

Hysteresis on the state, not just a cooldown on the greeting — the two are
different claims and only one of them is about the world:

  • separate enter/exit radii (e.g. enter at 0.15 km, leave only past ~0.30 km),
    and/or require N consecutive fresh samples on the same side before flipping;
  • ignore samples whose gps_accuracy_m exceeds the radius they are being
    compared against — a 34 m-accurate fix is fine against a 150 m radius, a
    200 m-accurate one is not evidence of anything;
  • keep the semantic-address branch (place == "at-dads") as-is; it does not
    jitter.

Deliberately not proposed: raising GREET_ARRIVAL_COOLDOWN_S. That would
hide the flap while leaving awareness["findmyhub"] and any future consumer
still reading a state that oscillates six times an hour.

Acceptance

  • A stationary tracker sitting on the radius boundary produces at most one
    person_arrived_home transition.
  • A genuine away→home arrival still fires promptly (the daemon-restart guard in
    _last_known_findmyhub is preserved — an empty cache still suppresses the
    first observation).
  • Unit test with a synthetic distance series that crosses the boundary
    repeatedly, asserting one transition.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions