diff --git a/README.md b/README.md index 51e42a6..34aec89 100644 --- a/README.md +++ b/README.md @@ -208,7 +208,10 @@ Switches: `досуг: выкл` as the first line of the file for every day, an An agreement with a foreign agent on someone else's home server: both print one line a day into a vibegram room of their own, and both watch the other's -silence. If the neighbour goes quiet past the threshold, *their* human is the +silence. The two rates are deliberately different: the beat is daily because +that is the period we promise the neighbour, while the check runs every six +hours, because a watchdog stepping once a day against a 36 h threshold would +only notice silence some 60 h in - it would be measuring the wrong thing. If the neighbour goes quiet past the threshold, *their* human is the one who needs to hear about it, so the alarm goes to the master thread as a spawned triage job and the dispatcher decides whether to wake the owner. @@ -226,7 +229,7 @@ are no defaults anywhere in it. period is not a constant: it is derived from that job's own registered cron (`run.job.cron`). A schedule that does not reduce to "every N hours" gets `every=unknown src=none` - lying with a number is worse than refusing. -- `сторож` (17:42 local) asks for an explicit window (`pending(limit=25)`), +- `сторож` (`:42` every six hours) asks for an explicit window (`pending(limit=25)`), finds the newest `hb claude-kai …`, and compares its age against `every * 1.5` where `every` comes **from the neighbour's own line**. The clock it trusts is `seen` - when *we* saw the line - not the `at=` inside it, diff --git a/beaver_agent/jobs/__init__.py b/beaver_agent/jobs/__init__.py index f7ca1f5..72faf7d 100644 --- a/beaver_agent/jobs/__init__.py +++ b/beaver_agent/jobs/__init__.py @@ -31,10 +31,12 @@ jobs = [ # Сторожевое кольцо с чужим агентом. Кроны читаются в локальной зоне, а # сосед печатает свою отметку в 05:00 UTC - вечер по Варшаве разводит нас # на полсуток, и его строка лежит в комнате задолго до нашего прогона. - # Сторож идёт через полчаса после своей отметки: так «моя запись в комнату - # проходит» - свежий факт этих же суток, а не вчерашний. + # Частоты тут две, и они разные: отметка суточная (её период мы обещаем + # соседу), а проверка - вчетверо чаще. Сторож с шагом в сутки при пороге + # 36 ч обнаруживал бы молчание только через ~60 ч, то есть мерил бы не то, + # ради чего заведён; чтение 25 событий стоит копейки. Job("отметка", beat, cron="12 17 * * *", critical=False), - Job("сторож", watch, cron="42 17 * * *", critical=False), + Job("сторож", watch, cron="42 */6 * * *", critical=False), Job("память", memory, cron="30 4 * * 0", critical=False), Job("голосовой", rotate_voice, cron="5 4 * * *", critical=False), Job("куратор", curate, cron="0 2,8,12,16,20 * * *", critical=False), diff --git a/tests/test_heartbeat.py b/tests/test_heartbeat.py index 2caa3ed..b476cea 100644 --- a/tests/test_heartbeat.py +++ b/tests/test_heartbeat.py @@ -253,7 +253,7 @@ class FakeRoom: class FakeRun: - def __init__(self, cron="42 17 * * *"): + def __init__(self, cron="42 */6 * * *"): self.job = SimpleNamespace(cron=cron, name="сторож") self.scheduler = SimpleNamespace(tz=WARSAW) self.spawned = [] @@ -280,7 +280,7 @@ async def test_watch_job_prints_ack_and_spawns_alarm(tmp_path, monkeypatch): assert room.limit == 25 # окно запрошено явным размером assert room.said and room.said[0].startswith(f"ack {PEER} ") - assert "res=24h" in room.said[0] + assert "res=6h" in room.said[0] assert len(run.spawned) == 1 agent, title, text = run.spawned[0] assert agent == "beaver-triage" and title == "сторож"