earned_time 0.9.0: tutor earner renamed automation -> tutor
Some checks are pending
dependency freshness / dependencies on newest stable (push) Waiting to run
md-naming / markdown naming convention (push) Waiting to run
pre-commit / pre-commit (${{ matrix.group }}) (flutter, flutter-analyze, ) (push) Waiting to run
pre-commit / pre-commit (${{ matrix.group }}) (mypy, mypy, ) (push) Waiting to run
pre-commit / pre-commit (${{ matrix.group }}) (pylint, pylint, ) (push) Waiting to run
pre-commit / pre-commit (${{ matrix.group }}) (rest, , mypy,pylint,flutter-analyze) (push) Waiting to run
Tests / test (push) Waiting to run
Tests / lock-popup-safety (push) Waiting to run
Tests / flutter test (shard ${{ matrix.shard }}) (0) (push) Waiting to run
Tests / flutter test (shard ${{ matrix.shard }}) (1) (push) Waiting to run
Tests / flutter test (shard ${{ matrix.shard }}) (2) (push) Waiting to run
Tests / flutter test (shard ${{ matrix.shard }}) (3) (push) Waiting to run
Tests / flutter test (shard ${{ matrix.shard }}) (4) (push) Waiting to run
Tests / flutter test (shard ${{ matrix.shard }}) (5) (push) Waiting to run
Tests / flutter test (shard ${{ matrix.shard }}) (6) (push) Waiting to run
Tests / flutter test (shard ${{ matrix.shard }}) (7) (push) Waiting to run
Tests / flutter test (shard ${{ matrix.shard }}) (8) (push) Waiting to run
Tests / flutter test (shard ${{ matrix.shard }}) (9) (push) Waiting to run
Tests / flutter-coverage (push) Blocked by required conditions
Tests / web-coverage (push) Waiting to run
repo-contract / repo contract (push) Waiting to run

Pins bumped at all four sites; tests follow TUTOR / tutor_bonus_units.
pylint 4.1.3 (freshness gate).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YADHKXzZhhePzQ2rNoXFcK
This commit is contained in:
Krzysztof Rudnicki 2026-10-12 16:38:18 +02:00
parent 4b65b7667b
commit 96ad163ee6
8 changed files with 18 additions and 19 deletions

View File

@ -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.8.0#subdirectory=earned_time
- earned-time @ git+https://github.com/kuhyx/utils@earned-time-v0.9.0#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.8.0#subdirectory=earned_time
- earned-time @ git+https://github.com/kuhyx/utils@earned-time-v0.9.0#subdirectory=earned_time
- freedays @ git+https://github.com/kuhyx/utils@freedays-v0.3.1#subdirectory=freedays
- mcp>=2,<3
- pydantic

View File

@ -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.8.0#subdirectory=earned_time",
"earned-time @ git+https://github.com/kuhyx/utils@earned-time-v0.9.0#subdirectory=earned_time",
]
[project.scripts]

View File

@ -4,13 +4,13 @@ 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.8.0#subdirectory=earned_time
earned-time @ git+https://github.com/kuhyx/utils@earned-time-v0.9.0#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
mypy==2.4.0
pre-commit==4.6.2
pylint==4.1.2
pylint==4.1.3
pytest==9.1.1
pytest-cov==7.1.0
pytest-randomly==5.0.0

View File

@ -30,9 +30,9 @@ def key(tmp_path: Path) -> Iterator[Path]:
@pytest.mark.usefixtures("key")
def test_the_real_tutor_rung_pays_legacy_blocks_as_15_minutes_up_to_60() -> None:
"""Legacy block rows (no ``detail.minutes``) pay 15 minutes each (0.8.0)."""
tutor = getattr(earned_time, "AUTOMATION_TUTOR", None)
tutor = getattr(earned_time, "TUTOR", None)
if tutor is None:
pytest.skip("installed earned_time predates the tutor (< 0.5)")
pytest.skip("installed earned_time predates TUTOR (< 0.9)")
cutover = earned_time.TUTOR_FROM.isoformat()
state: dict[str, Any] = {}
adjust = MagicMock(return_value=True)
@ -53,15 +53,15 @@ def test_the_real_tutor_rung_pays_legacy_blocks_as_15_minutes_up_to_60() -> None
apply_counted_bonus(state, adjust, tutor, cutover)
assert [c.args[0] for c in adjust.call_args_list] == [15, 15, 15, 15]
# Units are minutes now: five block rows count 75; the cap stops the pay.
assert state["automation_bonus_units"] == {"date": cutover, "units": 75}
assert state["tutor_bonus_units"] == {"date": cutover, "units": 75}
@pytest.mark.usefixtures("key")
def test_the_real_tutor_rung_pays_each_credited_minute() -> None:
"""A per-minute row (``detail.minutes``) pays exactly its minutes."""
tutor = getattr(earned_time, "AUTOMATION_TUTOR", None)
tutor = getattr(earned_time, "TUTOR", None)
if tutor is None:
pytest.skip("installed earned_time predates the tutor (< 0.5)")
pytest.skip("installed earned_time predates TUTOR (< 0.9)")
cutover = earned_time.TUTOR_FROM.isoformat()
state: dict[str, Any] = {}
adjust = MagicMock(return_value=True)
@ -71,4 +71,4 @@ def test_the_real_tutor_rung_pays_each_credited_minute() -> None:
write_ledger(tutor, [signed(row)])
apply_counted_bonus(state, adjust, tutor, cutover)
adjust.assert_called_once_with(7)
assert state["automation_bonus_units"] == {"date": cutover, "units": 7}
assert state["tutor_bonus_units"] == {"date": cutover, "units": 7}

View File

@ -28,7 +28,7 @@ if TYPE_CHECKING:
_DAY = date(2026, 10, 10)
_ISO = _DAY.isoformat()
_FLAT = {"leetcode": False, "reading": False, "anki": False, "automation": False}
_FLAT = {"leetcode": False, "reading": False, "anki": False, "tutor": False}
def at(hhmm: str, day: date = _DAY) -> float:

View File

@ -58,7 +58,7 @@ def _credits(fill: Callable[[date], date | None] | None) -> Callable[[date], Cre
def _new_tutor_beside_mature(day: date) -> Credits:
mature = {e.name: date(2026, 8, 1) for e in registry_for(day)}
return {**mature, "automation": None}
return {**mature, "tutor": None}
_MATURITY: Mapping[str, Callable[[date], Credits]] = {

View File

@ -21,7 +21,7 @@ if TYPE_CHECKING:
_DAY = date(2026, 10, 10)
_ISO = _DAY.isoformat()
_FLAT = {"leetcode": False, "reading": False, "anki": False, "automation": False}
_FLAT = {"leetcode": False, "reading": False, "anki": False, "tutor": False}
def at(hhmm: str) -> float:

View File

@ -41,10 +41,9 @@ def tutor(monkeypatch: pytest.MonkeyPatch, tmp_path: Path) -> Iterator[Any]:
Yields:
The tutor earner, penalised from ``_CUTOVER``.
"""
item = replace(earned_time.AUTOMATION_TUTOR, penalty_from=_CUTOVER)
item = replace(earned_time.TUTOR, penalty_from=_CUTOVER)
swapped = tuple(
item if e is earned_time.AUTOMATION_TUTOR else e
for e in earned_time.TUTOR_EARNERS
item if e is earned_time.TUTOR else e for e in earned_time.TUTOR_EARNERS
)
monkeypatch.setattr(earned_time, "TUTOR_EARNERS", swapped)
monkeypatch.setattr("earned_time._registry.TUTOR_FROM", _CUTOVER)
@ -82,7 +81,7 @@ class TestConfirmedButNeverCredited:
def test_the_map_says_never(self) -> None:
starts = first_credits(registry(_CUTOVER), _CUTOVER)
assert starts is not None
assert starts["automation"] is None
assert starts["tutor"] is None
def test_the_tutor_penalty_is_on_the_gaming_base_unpaid(self) -> None:
answers = {e.name: 0 for e in registry(_CUTOVER)}
@ -103,7 +102,7 @@ class TestConfirmedButNeverCredited:
starts = first_credits(registry(_CUTOVER), _CUTOVER)
wired = derive(DayInputs(_CUTOVER, answers, first_credits=starts))
unwired = derive(DayInputs(_CUTOVER, answers))
tutor = earned_time.AUTOMATION_TUTOR
tutor = earned_time.TUTOR
assert tutor.shutdown_for(tutor.max_units or 1, _CUTOVER) == 60
assert wired.resolution.base.shutdown_minutes == (
unwired.resolution.base.shutdown_minutes