mirror of
https://github.com/kuhyx/screen-locker.git
synced 2026-11-10 20:07:16 +01:00
deps: earned_time 0.6.1 (tutor cutover; new-gate rungs spare the floor)
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
3a2ad0245c
commit
2140ca5180
@ -134,7 +134,7 @@ repos:
|
||||
# than merely an old one.
|
||||
- gatelock @ git+https://github.com/kuhyx/utils@gatelock-v0.9.0#subdirectory=gatelock
|
||||
- crdt-sync @ git+https://github.com/kuhyx/utils@crdt-sync-v0.9.2#subdirectory=crdt-sync
|
||||
- earned-time @ git+https://github.com/kuhyx/utils@earned-time-v0.6.0#subdirectory=earned_time
|
||||
- earned-time @ git+https://github.com/kuhyx/utils@earned-time-v0.6.1#subdirectory=earned_time
|
||||
- freedays @ git+https://github.com/kuhyx/utils@freedays-v0.3.1#subdirectory=freedays
|
||||
- mcp>=2,<3
|
||||
- pydantic
|
||||
@ -155,7 +155,7 @@ repos:
|
||||
# than merely an old one.
|
||||
- gatelock @ git+https://github.com/kuhyx/utils@gatelock-v0.9.0#subdirectory=gatelock
|
||||
- crdt-sync @ git+https://github.com/kuhyx/utils@crdt-sync-v0.9.2#subdirectory=crdt-sync
|
||||
- earned-time @ git+https://github.com/kuhyx/utils@earned-time-v0.6.0#subdirectory=earned_time
|
||||
- earned-time @ git+https://github.com/kuhyx/utils@earned-time-v0.6.1#subdirectory=earned_time
|
||||
- freedays @ git+https://github.com/kuhyx/utils@freedays-v0.3.1#subdirectory=freedays
|
||||
- mcp>=2,<3
|
||||
- pydantic
|
||||
|
||||
@ -10,7 +10,7 @@ dependencies = [
|
||||
"gatelock @ git+https://github.com/kuhyx/utils@gatelock-v0.9.0#subdirectory=gatelock",
|
||||
"crdt-sync @ git+https://github.com/kuhyx/utils@crdt-sync-v0.9.2#subdirectory=crdt-sync",
|
||||
"freedays @ git+https://github.com/kuhyx/utils@freedays-v0.3.1#subdirectory=freedays",
|
||||
"earned-time @ git+https://github.com/kuhyx/utils@earned-time-v0.6.0#subdirectory=earned_time",
|
||||
"earned-time @ git+https://github.com/kuhyx/utils@earned-time-v0.6.1#subdirectory=earned_time",
|
||||
]
|
||||
|
||||
[project.scripts]
|
||||
|
||||
@ -4,7 +4,7 @@ bandit==1.9.4
|
||||
codespell==2.4.3
|
||||
coverage==7.16.2
|
||||
crdt-sync @ git+https://github.com/kuhyx/utils@crdt-sync-v0.9.2#subdirectory=crdt-sync
|
||||
earned-time @ git+https://github.com/kuhyx/utils@earned-time-v0.6.0#subdirectory=earned_time
|
||||
earned-time @ git+https://github.com/kuhyx/utils@earned-time-v0.6.1#subdirectory=earned_time
|
||||
freedays @ git+https://github.com/kuhyx/utils@freedays-v0.3.1#subdirectory=freedays
|
||||
gatelock @ git+https://github.com/kuhyx/utils@gatelock-v0.9.0#subdirectory=gatelock
|
||||
mcp==2.3.0
|
||||
|
||||
@ -199,8 +199,14 @@ class TestProjectionFollowsTheCut:
|
||||
def base(day: date) -> int:
|
||||
# From the sleep ladder (Sat 2026-10-10) the registry prices the
|
||||
# day itself; before it, the cut from Tuesday holds.
|
||||
# The registry in force that day (the tutor cutover switches it).
|
||||
return earned_time.base_for(day, _earned.registry(day)).shutdown_minutes
|
||||
# The registry in force that day (the tutor cutover switches it),
|
||||
# and from today on its first credits: a never-paid tutor spares
|
||||
# the floor (earned_time 0.6.1).
|
||||
earners = _earned.registry(day)
|
||||
starts = _earned.first_credits(earners, day)
|
||||
return earned_time.base_for(
|
||||
day, earners, first_credits=starts
|
||||
).shutdown_minutes
|
||||
|
||||
week = [date(2026, 10, 5 + i) for i in range(7)]
|
||||
assert [base(d) for d in week[1:4]] == [lowered] * 3
|
||||
|
||||
@ -17,7 +17,7 @@ import earned_time
|
||||
import pytest
|
||||
|
||||
from screen_locker._day import today_str
|
||||
from screen_locker._earned import flat_earners, hhmm, registry
|
||||
from screen_locker._earned import first_credits, gate_earners, hhmm, registry
|
||||
from screen_locker._shutdown_base import (
|
||||
_apply_flat_bonus,
|
||||
apply_flat_bonuses_if_new,
|
||||
@ -70,12 +70,14 @@ class TestBaseHour:
|
||||
minus every earner's first unit, not 20:00 minus the cuts, so the day
|
||||
is priced by the registry and only the "stays lowered" part is ours.
|
||||
The Anki waiver (``ANKI_WAIVED_FROM``) raises that floor to 19:50, so
|
||||
"lowered" means below the uncut 20:00, not at or below 19:00.
|
||||
"lowered" means below the uncut 20:00, not at or below 19:00; a gate
|
||||
that never paid out (the tutor) spares it (earned_time 0.6.1).
|
||||
"""
|
||||
later = date(2030, 1, 1)
|
||||
expected = earned_time.base_for(later, registry(later)).shutdown_minutes
|
||||
assert base_minutes(later) == expected
|
||||
assert expected < base_minutes(date(2026, 9, 30))
|
||||
starts = first_credits(registry(later), later)
|
||||
base = earned_time.base_for(later, registry(later), first_credits=starts)
|
||||
assert base_minutes(later) == base.shutdown_minutes
|
||||
assert base.shutdown_minutes < base_minutes(date(2026, 9, 30))
|
||||
|
||||
def test_default_is_the_local_today(self) -> None:
|
||||
assert base_minutes() == base_minutes(datetime.now().astimezone().date())
|
||||
@ -163,7 +165,7 @@ class TestResetIncludesReading:
|
||||
reset_to_base_if_new_day(tmp_path / "state.json", _mixin())
|
||||
base = base_minutes()
|
||||
later = "".join(
|
||||
f" + 0h {e.label}" for e in flat_earners() if e.name not in _NAMED
|
||||
f" + 0h {e.label}" for e in gate_earners() if e.name not in _NAMED
|
||||
)
|
||||
assert (
|
||||
f"Daily base reset: {hhmm(base + 60)} (base {hhmm(base)} + 0h workout + "
|
||||
|
||||
@ -1,9 +1,10 @@
|
||||
"""The Automation tutor on a simulated cutover: penalty start and grace floor.
|
||||
|
||||
``earned_time.TUTOR_FROM`` is a far-future sentinel, so the cutover is
|
||||
simulated on a fixed day: the registry's cutover (bound by value in
|
||||
``_registry`` and ``_ladder``) and the tutor's own ``penalty_from`` move to
|
||||
:data:`_CUTOVER`. Nothing is written outside the suite's redirected home.
|
||||
``earned_time.TUTOR_FROM`` is 2026-10-10 since earned_time 0.6.1; the
|
||||
fixture still pins the registry's cutover (bound by value in ``_registry`` and
|
||||
``_ladder``) and the tutor's own ``penalty_from`` to :data:`_CUTOVER`, so the
|
||||
suite does not move if a later release moves the cutover. Nothing is written
|
||||
outside the suite's redirected home.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
@ -92,13 +93,25 @@ class TestNeverCredited:
|
||||
assert wired.resolution.base.gaming_minutes == (
|
||||
unwired.resolution.base.gaming_minutes + cut
|
||||
)
|
||||
# KNOWN GAP (earned_time 0.6.0): on the sleep ladder the shutdown
|
||||
# floor is the ceiling minus every registered earner's full pay and
|
||||
# never asks whether a penalty is in force, so a never-credited tutor
|
||||
# still costs its shutdown minutes. A fix there flips this assertion.
|
||||
|
||||
def test_no_tutor_rung_off_the_shutdown_floor(self) -> None:
|
||||
"""earned_time 0.6.1: a gate whose penalty has not started spares the floor.
|
||||
|
||||
Unwired, the tutor's penalty starts at ``penalty_from`` and its full
|
||||
rung (13+13+12+12) comes off the ladder floor; wired, the never-paid
|
||||
tutor costs no shutdown either, so the floor stays at 19:50.
|
||||
"""
|
||||
answers = {e.name: 0 for e in registry(_CUTOVER)}
|
||||
starts = first_credits(registry(_CUTOVER), _CUTOVER)
|
||||
wired = derive(DayInputs(_CUTOVER, answers, first_credits=starts))
|
||||
unwired = derive(DayInputs(_CUTOVER, answers))
|
||||
tutor = earned_time.AUTOMATION_TUTOR
|
||||
rung = tutor.shutdown_for(tutor.max_units or 1, _CUTOVER)
|
||||
assert rung == 50
|
||||
assert wired.resolution.base.shutdown_minutes == (
|
||||
unwired.resolution.base.shutdown_minutes
|
||||
unwired.resolution.base.shutdown_minutes + rung
|
||||
)
|
||||
assert wired.resolution.base.shutdown_minutes == 19 * 60 + 50
|
||||
|
||||
|
||||
@pytest.mark.usefixtures("tutor")
|
||||
|
||||
Loading…
Reference in New Issue
Block a user