Commit Graph

10 Commits

Author SHA1 Message Date
ea3f78b711 deps(pypi): newest stable pins in every Python subproject
pre-commit 4.7.0, pylint 4.1.3 and ruff 0.17.2 across crdt-sync,
earned_time, freedays, gatelock, music_theory and sickdays; numpy 2.5.4 in
music_theory (requirements, pyproject and the mypy/pylint hook deps). The
ruff-pre-commit rev follows to v0.17.2 in those configs and vmbox's, so the
hook lints with the version requirements.txt pins. No Python lockfiles exist.
All six suites at 100% coverage; every package's pre-commit config green.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NBScELfMUiieK9ManFbS3Y
2026-10-22 21:42:26 +02:00
cca7dd680f lint: clear what ruff 0.17.2 and sickdays' pylint hook flag
- sickdays: normalise_keep raises TypeError (not ValueError) for a bare
  string -- ruff 0.17.2 TRY004; it is a caller type error, the CLI always
  passes a list.
- sickdays: remove_sick_day and set_kept_lockers share require_sick(), which
  clears pylint R0801 (duplicate-code, 9.99/10 under --fail-under=10).
- repo_contract/web.py: tomllib sorts as stdlib under ruff 0.17.x (I001).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NBScELfMUiieK9ManFbS3Y
2026-10-22 21:42:16 +02:00
7f2443ce48 sickdays.gui: refuse a write while a background sync is in flight
Some checks failed
dependency freshness / dependencies on newest stable (push) Waiting to run
file length / file-length (push) Waiting to run
md-naming / markdown naming convention (push) Waiting to run
palette drift / palette (push) Waiting to run
repo-contract / repo contract (push) Waiting to run
shared gate tests / gate unit tests (push) Waiting to run
sickdays pre-commit / pre-commit (push) Has been cancelled
sickdays tests / test (push) Has been cancelled
sync() reads the pool, waits on the network, then writes its merged copy
back without a lock. A second write landing inside that window was
silently overwritten. Marking day A and then clearing day B within a
second or two lost the clear, so B stayed a sick day.

Every background sync now holds a module lock, and each write tries it
without blocking. A click that lands mid-sync shows "Syncing -- try again
in a moment." in the panel and calls on_saved with nothing. The phone's
picker avoids the same race by making the grid inert while it syncs.

The red-first test pins the lost write with a sync stub that reads, waits,
then writes back.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PAhbNszs5JKdtnD4uBi3xp
2026-10-17 13:51:26 +02:00
bd64b27109 sickdays 0.3.0: desktop sick-day picker and embeddable KeepPanel
`sickdays gui` opens a Tk window modelled on the phone's Sick days screen:
a Monday-first month calendar (cat-2 fill, a dot for kept lockers, past
days faded and inert), a five-switch panel for the clicked day, and the
"Marked sick" list with "Kept: ..." lines. A free day is refused with the
phone's wording. Each write starts sync_quietly() on a non-daemon thread.
The window class is "sickdays" and Escape closes it.

sickdays.gui.KeepPanel is the panel on its own, a plain tk.Frame with no
root, mainloop, grab or wm_* call, so screen-locker can embed it under its
lock-screen grab. `import sickdays` and the rest of the CLI never import
tkinter: the CLI loads the gui module by name only for `gui`, and a
subprocess test checks this.

The tests drive real Tk on a withdrawn root, and CI now runs them under
xvfb-run, as gatelock's CI does. Adds a sickdays.desktop launcher, which
install.sh installs.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PAhbNszs5JKdtnD4uBi3xp
2026-10-17 13:47:26 +02:00
0a51c9998d sickdays 0.2.0: per-locker keep set on a sick day
Some checks are pending
dependency freshness / dependencies on newest stable (push) Waiting to run
file length / file-length (push) Waiting to run
md-naming / markdown naming convention (push) Waiting to run
palette drift / palette (push) Waiting to run
repo-contract / repo contract (push) Waiting to run
shared gate tests / gate unit tests (push) Waiting to run
sickdays pre-commit / pre-commit (push) Waiting to run
sickdays tests / test (push) Waiting to run
A sick day may keep some lockers active: a locker stands down iff the day
is sick and the locker is not in that day's keep set. New record field
`keep` = [sorted, de-duplicated locker ids, hlc].

- add/remove write state, actor and keep on one tick (keep=[] on a clear),
  so a stale keep cannot survive a clear + re-add.
- set_kept_lockers re-stamps only keep and actor; state keeps its clock, so
  a keep edit never re-asserts sick over a concurrent clear. Ticks are
  issued after every clock already on the record (clock-skew safe).
- Reading: no keep (0.1.0) or keep older than state -> keep nothing;
  malformed keep -> every locker kept (fail closed, logged); unknown ids
  ignored.
- API: stands_down, kept_lockers, set_kept_lockers, add_sick_day(keep=),
  SickDay.keep, LOCKER_* / LOCKERS. is_sick_day unchanged.
- CLI: add --keep, keep DAY [LOCKER ...], list shows keep, check --locker.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PAhbNszs5JKdtnD4uBi3xp
2026-10-16 15:08:06 +02:00
c6838bc865 sickdays: ship py.typed so consumers type-check against it
Some checks failed
dependency freshness / dependencies on newest stable (push) Waiting to run
file length / file-length (push) Waiting to run
md-naming / markdown naming convention (push) Waiting to run
palette drift / palette (push) Waiting to run
repo-contract / repo contract (push) Waiting to run
shared gate tests / gate unit tests (push) Waiting to run
sickdays pre-commit / pre-commit (push) Waiting to run
sickdays tests / test (push) Waiting to run
earned_time pre-commit / pre-commit (push) Has been cancelled
earned_time tests / test (push) Has been cancelled
freedays pre-commit / pre-commit (push) Has been cancelled
freedays tests / test (push) Has been cancelled
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Xfj1sxKH9VBPqpcAni8Jry
2026-10-16 13:37:31 +02:00
221c481644 sickdays: pin freedays-v0.4.0 (sick-day conflict check)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Xfj1sxKH9VBPqpcAni8Jry
2026-10-16 08:37:13 +02:00
c490ba5992 freedays 0.4.0: mark refuses a sick day (call-time sickdays import, allow+warn if absent)
Also fixes a ~5% flake in the hmac round-trip test (freedays and sickdays):
load_key strips whitespace, so a random key edged with a whitespace byte
never compared equal to the raw bytes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Xfj1sxKH9VBPqpcAni8Jry
2026-10-16 08:28:44 +02:00
af8c6c78bf sickdays: a non-UTF-8 pool file fails closed instead of raising
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Xfj1sxKH9VBPqpcAni8Jry
2026-10-16 08:25:00 +02:00
894d76f811 sickdays 0.1.0 + earned_time 0.11.0: uncapped sick-day pool, 20:00 sick shutdown floor
sickdays: per-date pool, no cap or reason, add/remove today or later only,
import_legacy for the migration. LWW state flip per date (no tombstones),
Firebase RTDB sickdays-sync/devices/<uuid>/sick_days.json, HMAC audit trail.
Readers fail closed (unreadable pool = not sick, logged); writers refuse a
corrupt pool. Free-day cross-check via a lazily imported freedays predicate.

earned_time: resolve(..., sick=True) floors the final shutdown at 20:00
(capped at the ceiling); gaming, terms and base unchanged. Result dataclasses
moved to _resolution.py for the line cap.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Xfj1sxKH9VBPqpcAni8Jry
2026-10-16 08:23:00 +02:00