Fixes a latent race: a bare alive heartbeat built an empty report whose SQL overwrote node_report.declared with '', and since current requires declared == sent, any heartbeat after a slow apply flipped the node to current:false and it never recovered. A fast apply (catalogue-small) beat the 60s alive tick, so it never surfaced there; a slow apply (heavy modules) is clobbered before settled() can see current:true. 12-line fix — a bare alive touches only last_seen, exactly as commit f0cff88's own message intended — plus a regression test that fails without it.
Lab-verified: the four-module apps wave (mongodb, unifi, marrytts, postgres) now reaches current:true and passes (SUITE_EXIT=0), where it previously stalled to the settle timeout. Would have bitten any slow multi-module install.
Fixes a latent race: a bare `alive` heartbeat built an empty report whose SQL overwrote `node_report.declared` with `''`, and since `current` requires `declared == sent`, any heartbeat after a slow apply flipped the node to `current:false` and it never recovered. A fast apply (catalogue-small) beat the 60s alive tick, so it never surfaced there; a slow apply (heavy modules) is clobbered before `settled()` can see `current:true`. 12-line fix — a bare alive touches only `last_seen`, exactly as commit f0cff88's own message intended — plus a regression test that fails without it.
**Lab-verified:** the four-module apps wave (mongodb, unifi, marrytts, postgres) now reaches `current:true` and passes (SUITE_EXIT=0), where it previously stalled to the settle timeout. Would have bitten any slow multi-module install.
https://claude.ai/code/session_01LrgweAeERJYBg88c5cKDzF
A node says it is there every minute and describes what it applied rarely,
and both went through Heard, which wrote every one down as a report. So a
bare alive replaced the node's last real apply with an empty one -- clearing
the declaration digest `current` is measured against, the carried ports a
push assigns around, and the clean-or-failed outcome. A node that had just
caught up read as behind within the minute, and never converged.
Whether it converged in time was a race the node's own apply set: the link's
one loop applies a declaration to completion before it can send the pending
heartbeat, so a fast apply (catalogue-small) leaves the digest standing the
~60s until the next beat -- long enough for the lab to see `current` -- while
a heavy wave whose apply outran the first beat (mongodb + unifi + marrytts)
had the alive fire milliseconds after the report and never showed `current`
at all, timing out settle even at 1200s.
Heard now returns after moving last_seen for a report that carries no account
of what the machine did -- nothing applied, nothing refused, nothing failed,
which is exactly a bare alive. A real report always carries one. This is what
the commit that began hearing alives said it did and did not: "a bare word
that a node is there moves last_seen and touches nothing else."
Claude-Session: https://claude.ai/code/session_01LrgweAeERJYBg88c5cKDzF
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Fixes a latent race: a bare
aliveheartbeat built an empty report whose SQL overwrotenode_report.declaredwith'', and sincecurrentrequiresdeclared == sent, any heartbeat after a slow apply flipped the node tocurrent:falseand it never recovered. A fast apply (catalogue-small) beat the 60s alive tick, so it never surfaced there; a slow apply (heavy modules) is clobbered beforesettled()can seecurrent:true. 12-line fix — a bare alive touches onlylast_seen, exactly as commit f0cff88's own message intended — plus a regression test that fails without it.Lab-verified: the four-module apps wave (mongodb, unifi, marrytts, postgres) now reaches
current:trueand passes (SUITE_EXIT=0), where it previously stalled to the settle timeout. Would have bitten any slow multi-module install.https://claude.ai/code/session_01LrgweAeERJYBg88c5cKDzF