Skip to content

fix(session): wire the GPS trigger, which armed and could never fire - #215

Merged
PurpleSentinel merged 1 commit into
mainfrom
feat/214-gps-trigger
Oct 17, 2026
Merged

PurpleSentinel merged 1 commit into
mainfrom
feat/214-gps-trigger

Conversation

@PurpleSentinel

Copy link
Copy Markdown
Contributor

Closes #214.

The defect

TRIGGER = GPS armed the device, showed WAITING FOR THE LINE, and nothing could ever start the session. Only two call sites reached begin_session: a manual START and IMU launch detection.

The mechanism existed and was never called. session/gate_automation.cpp starts on pit exit and stops on pit entry, gated on settings session_trigger.cpp:11 already derives from the trigger, and it is host-tested. The crossings were already arriving — drain_timing_output() read them off the timing queue and discarded them in an empty while loop.

Two things beyond the missing call

The duration. SessionController::start takes its duration from the configuration already saved, and that was only ever written inside begin_session. A crossing-started session would have run for SessionConfiguration's own twenty-minute default rather than the driver's setting — plausible, and wrong for anyone who set something else. Configuration now follows the settings: written at boot and whenever a duration is edited, but not mid-session, since a running session keeps the duration it began with.

I initially wrote this up as a zero-length session, which was wrong — the struct defaults to 20 minutes. My own test caught it when == 0 failed; the issue carries the correction.

The completion reason. The summary hardcoded driver_stop. Harmless while that was the only way a session could end; a pit entry can end one now, and a record saying the driver stopped when they did not is worse than no record of how it ended. It is taken from the controller, which already sets CompletionReason::pit_entry.

Decisions worth flagging

Crossings are offered to the automation unconditionally, not only while armed on GPS. The two settings it consults are the authority: pit-exit auto-start is derived from the trigger, and pit-entry auto-stop is its own setting a driver can want with any trigger. Gating in the router as well would put one decision in two places.

stop_from_pit_entry goes straight to review having already refreshed the session times, so the summary is captured after it rather than before as the driver-stop path does, and confirm_stop is not part of this route.

Every outcome is logged except the start and finish gates, which come through the same queue several times a lap. A pit exit that did not start a session is otherwise indistinguishable from no pit exit, and the reasons differ: the setting is off, a session was already running, or the controller refused the transition.

Scope: lap timing is untouched

Lap timing runs in the timing task, consumes the fix queue and detects start/finish crossings itself. gate_automation only ever looks at pit_exit and pit_entry. Laps were always timed correctly with GPS selected — only the session clock never started.

Verification

  • make check → CHECK_RC=0
  • firmware builds clean, 0xfdf00, 32% free
  • make repo-check passes
  • New host tests: an unconfigured controller taking the struct default, a configured one honouring 35 minutes rather than it, a second pit exit not restarting a running session, and a pit entry ending one with its own completion reason

Confirming it against a real pit exit needs a circuit, so device-side acceptance waits on #204's gate capture. The arming, configuration and logging paths are all bench-provable.

🤖 Generated with Claude Code

Choosing TRIGGER = GPS armed the device, showed WAITING FOR THE LINE, and
nothing could start the session. Only two call sites reached begin_session: a
manual START and IMU launch detection.

The mechanism was already there. session/gate_automation.cpp starts on pit exit
and stops on pit entry, gated on settings that session_trigger.cpp already
derives from the trigger, and it is host-tested. The crossings were already
arriving - drain_timing_output read them off the queue and threw them away in an
empty while loop. The two halves had never been introduced to each other.

Offered to the automation unconditionally rather than only while armed on GPS,
because the two settings it consults are the authority on whether it should act:
pit-exit auto-start is derived from the trigger, and pit-entry auto-stop is its
own setting a driver can want with any trigger. Gating in the router as well
would put the decision in two places.

Two things had to change beyond the missing call.

SessionController::start takes its duration from the configuration already
saved, and that was only ever written inside begin_session. A crossing-started
session would therefore have run for SessionConfiguration's own default of
twenty minutes rather than the duration the driver set - plausible, and wrong
for anyone who set something else, which is the kind of wrong that does not look
wrong. Configuration now follows the settings instead of the act of starting:
written at boot and again whenever a duration is edited, but not mid-session,
since a running session keeps the duration it began with.

The summary's completion reason was hardcoded to driver_stop. Harmless while a
driver stop was the only way a session could end; a pit entry can end one now,
and a record saying the driver stopped when they did not is worse than no record
of how it ended. It is taken from the controller, which already sets
CompletionReason::pit_entry.

stop_from_pit_entry goes straight to review having refreshed the session times,
so the summary is captured after it rather than before as the driver-stop path
does, and confirm_stop is not part of this route.

Every automation outcome is logged except the start and finish gates, which come
through the same queue several times a lap. A pit exit that did not start a
session is otherwise indistinguishable from no pit exit, and the reasons differ:
the setting is off, a session was already running, or the controller refused.

Tests cover an unconfigured controller taking the struct default, a configured
one honouring 35 minutes rather than it, a second pit exit not restarting a
running session, and a pit entry ending one with its own completion reason. The
first assertion was written expecting zero and corrected when it failed.

Closes #214

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@PurpleSentinel
PurpleSentinel merged commit 66c55a1 into main Oct 17, 2026
3 checks passed
@PurpleSentinel
PurpleSentinel deleted the feat/214-gps-trigger branch October 17, 2026 13:57
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

The GPS session trigger arms and never fires: gate automation is built, tested and never called

1 participant