Skip to content

Release 2.0.0 - #34

Merged
SeanathanVT merged 10 commits into
mainfrom
development
Oct 30, 2026
Merged

SeanathanVT merged 10 commits into
mainfrom
development

Conversation

@SeanathanVT

Copy link
Copy Markdown
Owner

Summary

Merges development into main for the 2.0.0 release. Full details are in CHANGELOG.md under [2.0.0].

Upgrading from 1.x

  • The new interface needs Safari 17.5, Chrome 123, Firefox 125, or newer on every device you use WalkingDad from. This is the only breaking change and the reason for the major version.
  • Everything else carries over. Routes and config keys were only added, renamed themes keep saved choices, and 1.9.0 still runs on 2.0.0's data if you need to roll back.

Added

Changed

Fixed

Internal

Testing

  • pytest: 561 passed on Python 3.10, 3.12, and 3.13. ruff check: clean.
  • CI:
  • Not verified: Windows (start_app.bat).

🤖 Generated with Claude Code

SeanathanVT and others added 10 commits October 9, 2026 09:32
…quence hardening (ROADMAP 2.6, 3.3, 3.13) (#24)

* docs(agents): follow common UI conventions and WCAG 2.2 AA

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* feat(ui): keyboard shortcuts, live tab title, and bounded dependencies (ROADMAP 2.6, 3.3, 3.13)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(ui): drop Start shortcut, swallow keys for disabled buttons, per-screen shortcut help (ROADMAP 3.3)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(ui): harden shortcuts and belt sequences from review (ROADMAP 3.3, 3.13, 3.18)

Shortcuts:
- Derive tooltips and the ? list in JS from aria-keyshortcuts; hotkey()
  now emits only that attribute
- Track keyboard vs mouse focus via capture-phase keydown/pointerdown
  instead of :focus-visible, so Space presses a keyboard-focused control
  but not one focused by a mouse click or script
- Restore focus when the ? list closes (WCAG 2.4.3); block keys while
  any modal is opening; only block for the phone menu while it's visible
- Create the help modal lazily so shortcuts survive a Bootstrap CDN failure

Belt sequences:
- Set belt_transitioning in the start/pause/resume/end routes, guarded by
  a generation counter so only the newest sequence can clear it; reset
  it when a new BLE loop starts
- Register sequences and speed changes before cancelling their
  predecessor, so they can't run concurrently or escape a disconnect
- _cancel_task: let the caller's own cancellation propagate, and cancel
  tasks from another event loop on that loop
- End Session stops the belt when it cancels an unfinished sequence
- Ignore speed changes while a belt sequence is in flight, checked and
  queued under the session lock
- Round, not truncate, km/h to device units (3.8 + 0.6 set 4.3)

Pages:
- Render buttons disabled while transitioning, re-enabling after 15 s if
  no stats update arrives; reload on back/forward-cache restore
- Single tab-title format, reset by the shutdown overlay, which also
  stops stats handling (connecting page included)

Docs: CHANGELOG, ROADMAP 2.6/3.3/3.13/3.18, new 1.8 (C2 behavior after
the physical remote, dropped back-to-back commands, app_speed target).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(ui): cancel pending speed change on pause/end; tidy review nits (ROADMAP 3.3, 3.13)

- Pause and End sequences cancel a queued speed change before stop_belt,
  so a step can't reach a stopped belt; correct the misleading comment
- _handle_disconnect: suppress RuntimeError from task.cancel() on a
  closed loop, so a stale task can't abort reconnect scheduling
- _cancel_task: log an error a task raised while being cancelled
- Settings hint uses parentheses instead of " -- "
- Tab title only reassigned when it changes

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* docs(roadmap): add 1.9 firmware version check and update

Opt-in check of the pad's firmware against KingSmith's latest, and
an over-the-air update replicating KS Fit, with no firmware bundled.
Starts with a spike decompiling KS Fit 6.6.0 to find the version
endpoint, image format, and update procedure; the update is built
only if that procedure proves safe. Also notes the C2's firmware
(6.1.2, software 6.3.0) in 1.8.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test(ble): make pending-speed-change cancel test version-independent

On Python 3.10, asyncio.wait_for returns normally when a cancel lands
as the lock is handed over, so asserting the step was cancelled failed
there (3.12+ raises). Model the actual hazard instead: a step waiting
on its predecessor outside the lock while Pause/End takes it. Passes
on 3.10 and 3.12; fails on both without the cancel.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* docs(agents): run new or changed tests on every CI Python version

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…ADMAP 1.9) (#25)

KS Fit's firmware check needs a KS Fit account session; record the
endpoint, image, and OTA findings for any future revisit. Ignore tmp/
so local research artifacts stay untracked.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
The WalkingPad turns itself off after sitting paused, and the app then
hid the session behind the Connecting/Connection Failed screens with no
way to resume or end it. Keep the paused screen up while disconnected,
with End Session and, once auto-reconnect gives up, Reconnect. Refuse
Resume while disconnected instead of queuing commands for a dead link.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…ent (ROADMAP 4.5) (#27)

* docs(roadmap): plan heart rate via BLE HRS, profiles first, speed-based calories (ROADMAP 3.10, 4.5)

Co-Authored-By: Claude <noreply@anthropic.com>

* feat: per-user profiles with walker picker and history filter (ROADMAP 4.5)

History mixed everyone's sessions together. Profiles attribute each
session to a walker and store optional height and weight for the
upcoming calorie model (ROADMAP 3.10). With no profiles, nothing
changes.

Storage:
- profiles table (id, name unique case-insensitive, height_m,
  weight_kg, created_at); schema_version 3, upgraded in place
- sessions.profile holds the profile id; 'default' (storage.UNASSIGNED)
  means Unassigned
- Profile CRUD, count_unassigned() and assign_unassigned();
  delete_profile() moves the profile's sessions to Unassigned in the
  same transaction instead of deleting them

Settings:
- Profiles section to add, rename, set height (whole ft/in) and
  weight (lb), and delete; stored in m and kg
- Inline errors take focus so they're visible below the long settings
  form; the add form keeps what was typed
- Creating a profile while Unassigned sessions exist offers to assign
  them, and offers again on later creations until they're claimed

Start:
- "Who's walking?" radio group: hidden with 0 profiles, preselected
  with 1, required with 2+. Uses native required validation instead of
  a disabled Start, since base.html re-enables Start on every SSE tick
  and a disabled button can't be focused or explain itself
- /start rejects a missing or stale profile_id (?walker_error=1); if
  profiles can't be read, the session still starts as Unassigned
- Active and paused screens show the walker; the Apple Health banner
  names them

History:
- Walker column and an All / name / Unassigned filter (?walker=)
- Export CSV follows the filter and appends a trailing walker column,
  so existing spreadsheets keep working
- Clear follows the filter and its confirm names the scope; a walker
  deleted in another tab returns 409 and clears nothing, instead of
  widening to all history

Co-Authored-By: Claude <noreply@anthropic.com>

* fix(profiles): scope unassigned claims to completed sessions, keep CSV unchanged without profiles (ROADMAP 4.5)

- count_unassigned()/assign_unassigned() only match completed sessions,
  so an in-progress or crash-interrupted walk isn't counted in the
  "Assign N sessions" prompt or re-tagged by it, matching how history
  and Clear already scope to completed rows
- Export CSV adds the trailing walker column only once profiles exist,
  so exports for households without profiles are unchanged
- README: finish the Profiles bullet (height/weight are for calorie
  estimates); CHANGELOG: the claim offer repeats on each new profile
  while unassigned sessions remain, not only the first; ROADMAP 4.5
  matches both

Co-Authored-By: Claude <noreply@anthropic.com>

* docs(roadmap): plan settings sub-pages and opt-in profiles (ROADMAP 3.20, 3.4)

Co-Authored-By: Claude <noreply@anthropic.com>

* fix(settings): move Profiles above the settings form so Save Settings ends the page (ROADMAP 4.5)

Profiles save per record with their own buttons, so placing the card
after Save Settings made Save look like it applied to them. Interim
layout until the settings sub-pages (ROADMAP 3.20).

Co-Authored-By: Claude <noreply@anthropic.com>

* fix(settings): reveal the add-profile form from an Add Profile button (ROADMAP 4.5)

An always-visible empty Name/Height/Weight row read as a blank profile.
Add Profile now expands the form (focus moves to Name), with Save
Profile and Cancel; it stays open after a failed create so the error and
typed values remain visible.

Co-Authored-By: Claude <noreply@anthropic.com>

* fix(ui): use dropdowns for the walker picker and history filter (ROADMAP 4.5)

Button rows don't scale past a few names. The Start picker becomes a
required "Who's walking?" select (placeholder with 2+ profiles,
preselected with 1); the history filter becomes a labelled select with
a Show button rather than filtering on change (WCAG 3.2.2). Drop the
chip CSS only the radios used.

Co-Authored-By: Claude <noreply@anthropic.com>

* fix(ui): pick the walker with "Start as <name>" under Start, defaulting to the last walker (ROADMAP 4.5)

The labelled dropdown above Start looked bolted onto the console.
Chosen from mockups: a quiet "as Sean ⌄" line under Start, a native
select styled as an inline link. It defaults to the most recent
session's walker (or the only profile), so most starts are one click;
with 2+ profiles and no previous walker it reads "choose a walker" and
stays required.

Co-Authored-By: Claude <noreply@anthropic.com>

* feat(ui): tie history to the selected walker, reassign in Edit, theme dropdown lists (ROADMAP 4.5)

History now follows the "as <walker>" picker ("Sean's Sessions"):
changing it swaps the table in place via GET /history?walker= (no
reload, announced to screen readers), replacing the filter dropdown.
Edit mode adds a Show scope (walker / All / Unassigned / names) and a
per-row Walker dropdown that reassigns completed sessions immediately
(POST /session/<id>/walker). Export CSV and Clear follow what's shown.

Dropdown lists use the active color theme where customizable selects
are supported (appearance: base-select; Chrome/Edge 135+, Safari 27),
falling back to native lists, dark in dark mode via color-scheme.
Delete and reassign use event delegation so swapped-in rows work.

Co-Authored-By: Claude <noreply@anthropic.com>

* fix(ui): drop the lingering focus ring after picking a walker with the mouse (ROADMAP 4.5)

A select keeps focus after a mouse pick and browsers then match
:focus-visible, leaving the ring on. Reuse the pointer/keyboard intent
tracker (ROADMAP 3.3) to expose data-intent on <html>, and hide the ring
on the walker selects only after pointer input; keyboard focus still
shows it.

Co-Authored-By: Claude <noreply@anthropic.com>

---------

Co-authored-by: Claude <noreply@anthropic.com>
* feat(storage): version and migrate stored data, refuse newer formats (ROADMAP 2.10)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(storage): refuse unusable schema versions cleanly, stamp imported rows (ROADMAP 2.10)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(storage): single unknown-key warning, non-WAL backups, WAL backup test (ROADMAP 2.10)

- Move the unknown config.json key warning out of prepare_data() into
  warn_unknown_settings(), called only from the server's startup. Both
  run.py and the server ran prepare_data, so the warning printed twice.
- Switch pre-upgrade DB backups to journal_mode=DELETE, so opening a
  backup doesn't leave -wal/-shm files in data/backups/.
- Test that a backup includes writes still in the WAL file (a connection
  held open with autocheckpoint off), which the earlier test couldn't
  reach because closing the connection checkpointed the WAL.
- README: an existing config.json gets its version on the next Settings
  save, backups are named after the database file, and quit any running
  copy before starting a newer one (the check only notices one on the
  same port).
- ROADMAP: record why a newer session_state.json is refused even when it
  says no walk is active.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…until restart (#29)

* fix(ble): give the connect its own 10 s timeout

- The connect plus service discovery shared the 5 s per-command timeout, so a slow macOS connect timed out mid-discovery
- Add _BLE_CONNECT_TIMEOUT_SECONDS = 10 for controller.run(); commands keep 5 s
- Tests for a slow connect succeeding and an over-limit connect still timing out

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(ble): disconnect after a failed connect and name the error

- A connect that failed partway left the half-open connection open, so the pad stopped advertising and every retry missed it until restart; disconnect before re-raising
- Log connect and auto-reconnect failures with the exception type (e.g. TimeoutError()) instead of a blank message
- Tests for both, plus a CHANGELOG entry

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* feat(ui): Ledger interface redesign (ROADMAP 3.21)

- Replace Bootstrap and the inline styles with static/ledger.css, themes.css and ledger.js
- Side rail, one title row per screen, panel layout; tablet breakpoint (3.17)
- Themed dialogs replace confirm()/alert() for shut down, delete and clear (3.15)
- Belt-speed ladder over the existing speed commands; connection screens show device and last walk
- Rename Frost to Holly and Harvest to Pumpkin, migrating saved choices
- VT theme follows brand.vt.edu; seasonal fonts and layer-relative ambient effects
- Update README, ROADMAP, CHANGELOG and screenshots

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(ui): Safari theme-switch hang and select arrow (ROADMAP 3.21)

- Drop position-try-fallbacks from the theme menu; with it, WebKit hangs when a theme's web font loads after the menu opened
- Draw the select arrow as one SVG chevron in both select paths; Safari clipped the gradient and rotated-border versions
- Add ROADMAP 3.23 (drag the belt ladder to any speed) as a follow-on

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(ui): keep Tide's crabs visible over the save bar and phone nav (ROADMAP 3.21)

- Lower the sticky Settings save bar below the ambient-effects layer
- End the effects layer at the phone bottom bar so crabs walk above it

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* feat(ui): use the WalkingDad logo as the rail mark (ROADMAP 3.21)

- Trace images/logo.png into one currentColor SVG path, so it follows the color theme
- Replace the mock's stick-figure-on-a-belt mark

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(ui): rail order, theme contrast and review fixes (ROADMAP 3.21)

- Rail: Walk, then Theme, Light/Dark and Shortcuts; Settings above Shut down; theme menu pinned beside the rail
- Themes: lighten or darken failing colors in OKLCH (hue kept) so dark buttons, hovers, the belt ladder and field borders meet WCAG AA; VT's dark selected preset gets an orange ring instead
- Add tests/test_theme_contrast.py checking every theme in both modes
- Phone Save bar clears the bottom nav; heart tooltip no longer clipped; Space pauses from the focused ladder; key hints hidden on touch screens
- Remove unused mock CSS and stale comments

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(lint): clear ruff findings that failed CI (ROADMAP 3.21)

- Parse the last-walk label with datetime.fromisoformat instead of a naive strptime (DTZ007)
- Same for the connecting-screen test's start time (DTZ001)
- Use re.DOTALL / re.MULTILINE in the theme contrast test (FURB167)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
- ruff --output-format=github turns each finding into a line annotation
- Name the install, lint and test steps; pytest -ra adds a short failure summary
- AGENTS.md: run every CI step, ruff included, before handing over


cd tmp/ci-branch

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
- Theme menu: presentational group labels and aria-haspopup="menu" on its button
- Selected preset stop is aria-current, not aria-pressed
- Focus moves to a neighbouring Delete (or Edit) after deleting a history row
- Add profile renders open so it works without JavaScript; script folds it on load

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
- Release Unreleased as 2.0.0 with an upgrade note for the new browser minimum
- Map the 9 items completed since 1.9.0 to [2.0.0] in ROADMAP
- Use bullet metadata (Status, Changelog, Priority, ...) for every ROADMAP item
- List the minimum browser versions in README requirements

Co-authored-by: Claude <noreply@anthropic.com>
@SeanathanVT
SeanathanVT merged commit 531b21b into main Oct 30, 2026
4 checks passed
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.

1 participant