fix(hands,jobs): watchdog window reads the room tail, not the pending cursor
This commit is contained in:
1 parent
91bd50a66d
commit
47c31ac356
6 files changed
+341
-37
No files matched your search
@@ -216,9 +216,9 @@ 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.
|
||||
|
||||
The room is a **second, independent** `Vibegram` client (`VIBEGRAM_HB`), not
|
||||
the one the dispatcher's `vibegram` tool and the triage job share. `/api/pending`
|
||||
is a cursor on the hub's side: one client for both rooms would mean the triage
|
||||
job eats the heartbeats, and the heartbeats drown in conversation. The model
|
||||
the one the dispatcher's `vibegram` tool and the triage job share: one client
|
||||
for both rooms would mean the triage job eats the heartbeats off the shared
|
||||
`/api/pending` cursor, and the heartbeats drown in conversation. The model
|
||||
gets no tool for this room - the watchdog is a mechanism.
|
||||
|
||||
The point of a watchdog is that it does not lie: an instrument that quietly
|
||||
@@ -229,8 +229,9 @@ 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.
|
||||
- `сторож` (`:42` every six hours) asks for an explicit window (`pending(limit=25)`),
|
||||
finds the newest `hb claude-kai …`, and compares its age against
|
||||
- `сторож` (`:42` every six hours) reads a window - **the last 25 messages of
|
||||
that room** (`Vibegram.window`) - 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,
|
||||
which is the neighbour's statement about himself.
|
||||
@@ -242,6 +243,18 @@ are no defaults anywhere in it.
|
||||
- Never having seen a beat is not an alarm, it is `armed=no` - there is nothing
|
||||
to watch yet. Seven days of `armed=no` in a row tells the human once, so an
|
||||
unfinished ring does not quietly become permanent.
|
||||
- The window is **not** `/api/pending`. That endpoint is a cursor: it hands a
|
||||
line over once and never again, so the neighbour's beat showed up in one run
|
||||
and was gone from the next one's window on a perfectly live room - the room
|
||||
held it the whole time. The tail comes from `/api/state?view=…` instead,
|
||||
which is already scoped to the room (hub event ids run across all rooms) and
|
||||
pages **forward** in chunks of 200 from `after=<id>`; the watchdog walks the
|
||||
history and keeps the last 25 *messages*, joins and leaves not counted.
|
||||
- **An unread window is not a reading.** If the history could not be walked to
|
||||
its end (the page budget ran out), the missing part is exactly the newest,
|
||||
so `win=25/partial` goes into the `ack`, the log says `окно не дочитано`, and
|
||||
no alarm is taken in that run: "nobody in the window" and "the window was
|
||||
never finished" must not look the same.
|
||||
- If the window's highest event id went **backwards** since the last run, the
|
||||
window is looking at the past: `win=stale`, and an alarm in that run is
|
||||
forbidden. Same for a hub that did not answer at all.
|
||||
|
||||
Reference in new issue
Block a user