Skip to content

emrg: GUI daemon heartbeat — periodic liveness probe + active reconnect (rant 2026-08-21T12:44:34) - #912

Merged
argszero merged 2 commits into
masterfrom
feature/gui-daemon-heartbeat
Aug 21, 2026
Merged

emrg: GUI daemon heartbeat — periodic liveness probe + active reconnect (rant 2026-08-21T12:44:34)#912
argszero merged 2 commits into
masterfrom
feature/gui-daemon-heartbeat

Conversation

@argszero

Copy link
Copy Markdown
Owner

Summary

Fixes rant 2026-08-21T12:44:34 (heartbeat item) — the GUI had no periodic liveness probe: daemon_client.js only sends one ping after connect ("不做轮询"), and the daemon connection's disconnected event was never listened to in main.js (the 2026-08-21 11:41 incident: GUI killed the daemon, never reconnected for 36 minutes, stale green status dot).

Changes

  • emrg/gui/main.js
    • startHeartbeat() / stopHeartbeat() / _heartbeatTick(): every 15s, ping the daemon connection via waitForPong(3000, conn); on no-pong or a dropped connection → scheduleReconnect() (existing exponential backoff, already pushes connected:false/reconnecting status to the renderer so the status dot is truthful).
    • waitForPong(timeoutMs, connOverride) — optional connection override so the heartbeat probes the daemon connection specifically (not the active session connection).
    • Wiring: heartbeat started on ensureConnected() success; stopped when scheduleReconnect() is entered (no double-trigger) and on window-all-closed.
  • emrg/gui/test/renderer.smoke.test.js — source-level test asserting the heartbeat wiring exists (constants, functions, waitForPong reuse, scheduleReconnect hook, start/stop wiring).
  • Agent.md — GUI test breakdown synced (renderer smoke 127→128, total 256→257).

Verification

  • GUI: npm test — 257 tests, 249 pass, 8 skipped (live daemon owns fixed port), 0 fail
  • node --check main.js OK
  • Python: uv run pytest tests/ — 987 passed + 1 skipped (incl. doc-count guards)
  • python -m emrg --help + import OK

@argszeroargszero left a comment

Copy link
Copy Markdown
OwnerAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ LGTM — cycle

Fresh review of the GUI heartbeat fix (rant 2026-08-21T12:44:34):

  • _heartbeatTick() every 15s probes the daemon connection: !conn.connected (dropped, no listener existed before) or no pong in 3s → stopHeartbeat() + scheduleReconnect() — reuses the existing exponential-backoff reconnect which already pushes connected:false/reconnecting to the renderer, so the status dot becomes truthful.
  • waitForPong(timeoutMs, connOverride) — optional conn param so the heartbeat pings the daemon connection specifically rather than the active session connection (which would otherwise hide a dead daemon while a session is open).
  • Wiring: startHeartbeat() on ensureConnected() success; stopHeartbeat() in scheduleReconnect() (prevents double-triggering) and window-all-closed. Heartbeat tick returns early when stopping.
  • Test: source-level assertion of the heartbeat wiring (constants, start/stop/tick functions, waitForPong + scheduleReconnect hooks, start/stop call sites); node --check main.js OK.
  • Verified: GUI npm test 257 (249 pass + 8 skipped), Python pytest 987 passed + 1 skipped incl. doc-count guards, import + CLI OK. CI test + test-windows PASS.

1/3.

@pm25coder

Copy link
Copy Markdown
Collaborator

Independent test report (Contributor, pm25coder):

@argszeroargszero left a comment

Copy link
Copy Markdown
OwnerAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ LGTM — cycle

2nd review (head 6f597ba — conflict-resolved merge of master into the branch, CI test + test-windows PASS on the resolved tree):

  • Fresh local verification: GUI npm test 256 total (248 pass / 0 fail / 8 skipped), pytest 987 passed + 1 skipped, import + CLI OK.
  • Conflict resolution: Agent.md GUI count now 256/127 (126 from #911's sidebar test removal + 1 new heartbeat test); no conflict markers left.
  • Code: _heartbeatTick() every 15s — detects dropped daemon-level connections (previously no listener existed) and triggers scheduleReconnect; pings via waitForPong(3000, conn); no-pong path closes the conn and reconnects; stopHeartbeat() on reconnect + window close prevents timer stacking. Sound.

2/3.

@argszeroargszero left a comment

Copy link
Copy Markdown
OwnerAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ LGTM — cycle

Re-review (head updated to 6f597ba — master merged in by a parallel cycle):

  • Heartbeat implementation intact after merge: startHeartbeat()/stopHeartbeat()/_heartbeatTick() — 15s ping on the daemon connection via waitForPong(3000, conn); no-pong or dropped connection → stopHeartbeat() + scheduleReconnect() (existing exponential backoff, renderer status dot updated truthfully).
  • waitForPong conn override targets the daemon connection specifically; heartbeat guarded against stopping; started on ensureConnected success, stopped on scheduleReconnect entry and window-all-closed.
  • Merge brought in #911 (sidebar removal) — no conflict; both features coexist. Agent.md GUI count on merged head = 256 (127 renderer smoke) matches actual npm test (256: 248 pass + 8 skipped).
  • Verified on merged head: GUI npm test 256, Python pytest 987 passed + 1 skipped, import OK. CI test + test-windows PASS (run 32449393467).

2/3.

@argszeroargszero left a comment

Copy link
Copy Markdown
OwnerAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ LGTM — cycle

3rd review (head 6f597ba, unchanged since 2/3):

  • Fresh local verification on this branch: GUI npm test 256 total (248 pass / 0 fail / 8 skipped), Python pytest 987 passed + 1 skipped, import + CLI OK.
  • Heartbeat implementation intact: _heartbeatTick() every 15s pings the daemon connection via waitForPong(3000, conn); dropped connection or no pong → stopHeartbeat() + scheduleReconnect() (exponential backoff, renderer status dot updated truthfully). Guarded against stopping; started on ensureConnected success; stopped on scheduleReconnect entry and window-all-closed.
  • CI test + test-windows PASS (run 32449393467).

3/3 — merging.

@argszero
argszero merged commit b959094 into masterAug 21, 2026
2 checks passed
@argszero
argszero deleted the feature/gui-daemon-heartbeat branch August 21, 2026 05:27
argszero added a commit that referenced this pull request Aug 21, 2026
Co-authored-by: EMRG Evolution <emrg@argszero.dev>
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@argszero@pm25coder
, 'i'); if (__m === '*' || __re.test(location.href)) { // Add copy buttons to all
 blocks
(function() {
function addCopyButtons() {
document.querySelectorAll('pre code').forEach(function(codeBlock) {
if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;
codeBlock.parentElement.setAttribute('data-copy-added', 'true');
var btn = document.createElement('button');
btn.textContent = 'Copy';
btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';
btn.onmouseover = function() { this.style.opacity = '1'; };
btn.onmouseout = function() { this.style.opacity = '0.7'; };
btn.onclick = function() {
navigator.clipboard.writeText(codeBlock.textContent).then(function() {
btn.textContent = 'Copied!';
setTimeout(function() { btn.textContent = 'Copy'; }, 1500);
});
};
codeBlock.parentElement.style.position = 'relative';
codeBlock.parentElement.appendChild(btn);
});
}
addCopyButtons();
// Re-run on dynamic content
var observer = new MutationObserver(addCopyButtons);
observer.observe(document.body, { childList: true, subtree: true });
})();
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
emrg: GUI daemon heartbeat — periodic liveness probe + active reconnect (rant 2026-08-21T12:44:34) by argszero · Pull Request #912 · argszero/emrg · GitHub
Skip to content

emrg: GUI daemon heartbeat — periodic liveness probe + active reconnect (rant 2026-08-21T12:44:34) - #912

Merged
argszero merged 2 commits into
masterfrom
feature/gui-daemon-heartbeat
Aug 21, 2026
Merged

emrg: GUI daemon heartbeat — periodic liveness probe + active reconnect (rant 2026-08-21T12:44:34)#912
argszero merged 2 commits into
masterfrom
feature/gui-daemon-heartbeat

Conversation

@argszero

Copy link
Copy Markdown
Owner

Summary

Fixes rant 2026-08-21T12:44:34 (heartbeat item) — the GUI had no periodic liveness probe: daemon_client.js only sends one ping after connect ("不做轮询"), and the daemon connection's disconnected event was never listened to in main.js (the 2026-08-21 11:41 incident: GUI killed the daemon, never reconnected for 36 minutes, stale green status dot).

Changes

  • emrg/gui/main.js
    • startHeartbeat() / stopHeartbeat() / _heartbeatTick(): every 15s, ping the daemon connection via waitForPong(3000, conn); on no-pong or a dropped connection → scheduleReconnect() (existing exponential backoff, already pushes connected:false/reconnecting status to the renderer so the status dot is truthful).
    • waitForPong(timeoutMs, connOverride) — optional connection override so the heartbeat probes the daemon connection specifically (not the active session connection).
    • Wiring: heartbeat started on ensureConnected() success; stopped when scheduleReconnect() is entered (no double-trigger) and on window-all-closed.
  • emrg/gui/test/renderer.smoke.test.js — source-level test asserting the heartbeat wiring exists (constants, functions, waitForPong reuse, scheduleReconnect hook, start/stop wiring).
  • Agent.md — GUI test breakdown synced (renderer smoke 127→128, total 256→257).

Verification

  • GUI: npm test — 257 tests, 249 pass, 8 skipped (live daemon owns fixed port), 0 fail
  • node --check main.js OK
  • Python: uv run pytest tests/ — 987 passed + 1 skipped (incl. doc-count guards)
  • python -m emrg --help + import OK

@argszeroargszero left a comment

Copy link
Copy Markdown
OwnerAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ LGTM — cycle

Fresh review of the GUI heartbeat fix (rant 2026-08-21T12:44:34):

  • _heartbeatTick() every 15s probes the daemon connection: !conn.connected (dropped, no listener existed before) or no pong in 3s → stopHeartbeat() + scheduleReconnect() — reuses the existing exponential-backoff reconnect which already pushes connected:false/reconnecting to the renderer, so the status dot becomes truthful.
  • waitForPong(timeoutMs, connOverride) — optional conn param so the heartbeat pings the daemon connection specifically rather than the active session connection (which would otherwise hide a dead daemon while a session is open).
  • Wiring: startHeartbeat() on ensureConnected() success; stopHeartbeat() in scheduleReconnect() (prevents double-triggering) and window-all-closed. Heartbeat tick returns early when stopping.
  • Test: source-level assertion of the heartbeat wiring (constants, start/stop/tick functions, waitForPong + scheduleReconnect hooks, start/stop call sites); node --check main.js OK.
  • Verified: GUI npm test 257 (249 pass + 8 skipped), Python pytest 987 passed + 1 skipped incl. doc-count guards, import + CLI OK. CI test + test-windows PASS.

1/3.

@pm25coder

Copy link
Copy Markdown
Collaborator

Independent test report (Contributor, pm25coder):

@argszeroargszero left a comment

Copy link
Copy Markdown
OwnerAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ LGTM — cycle

2nd review (head 6f597ba — conflict-resolved merge of master into the branch, CI test + test-windows PASS on the resolved tree):

  • Fresh local verification: GUI npm test 256 total (248 pass / 0 fail / 8 skipped), pytest 987 passed + 1 skipped, import + CLI OK.
  • Conflict resolution: Agent.md GUI count now 256/127 (126 from #911's sidebar test removal + 1 new heartbeat test); no conflict markers left.
  • Code: _heartbeatTick() every 15s — detects dropped daemon-level connections (previously no listener existed) and triggers scheduleReconnect; pings via waitForPong(3000, conn); no-pong path closes the conn and reconnects; stopHeartbeat() on reconnect + window close prevents timer stacking. Sound.

2/3.

@argszeroargszero left a comment

Copy link
Copy Markdown
OwnerAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ LGTM — cycle

Re-review (head updated to 6f597ba — master merged in by a parallel cycle):

  • Heartbeat implementation intact after merge: startHeartbeat()/stopHeartbeat()/_heartbeatTick() — 15s ping on the daemon connection via waitForPong(3000, conn); no-pong or dropped connection → stopHeartbeat() + scheduleReconnect() (existing exponential backoff, renderer status dot updated truthfully).
  • waitForPong conn override targets the daemon connection specifically; heartbeat guarded against stopping; started on ensureConnected success, stopped on scheduleReconnect entry and window-all-closed.
  • Merge brought in #911 (sidebar removal) — no conflict; both features coexist. Agent.md GUI count on merged head = 256 (127 renderer smoke) matches actual npm test (256: 248 pass + 8 skipped).
  • Verified on merged head: GUI npm test 256, Python pytest 987 passed + 1 skipped, import OK. CI test + test-windows PASS (run 32449393467).

2/3.

@argszeroargszero left a comment

Copy link
Copy Markdown
OwnerAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ LGTM — cycle

3rd review (head 6f597ba, unchanged since 2/3):

  • Fresh local verification on this branch: GUI npm test 256 total (248 pass / 0 fail / 8 skipped), Python pytest 987 passed + 1 skipped, import + CLI OK.
  • Heartbeat implementation intact: _heartbeatTick() every 15s pings the daemon connection via waitForPong(3000, conn); dropped connection or no pong → stopHeartbeat() + scheduleReconnect() (exponential backoff, renderer status dot updated truthfully). Guarded against stopping; started on ensureConnected success; stopped on scheduleReconnect entry and window-all-closed.
  • CI test + test-windows PASS (run 32449393467).

3/3 — merging.

@argszero
argszero merged commit b959094 into masterAug 21, 2026
2 checks passed
@argszero
argszero deleted the feature/gui-daemon-heartbeat branch August 21, 2026 05:27
argszero added a commit that referenced this pull request Aug 21, 2026
Co-authored-by: EMRG Evolution <emrg@argszero.dev>
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@argszero@pm25coder
, 'i'); if (__m === '*' || __re.test(location.href)) { // Force GitHub README to respect dark mode (function() { var style = document.createElement('style'); style.textContent = ' .markdown-body { color-scheme: dark light; } .markdown-body pre { background: #161b22 !important; } .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; } .markdown-body table th, .markdown-body table td { border-color: #30363d !important; } .markdown-body img { background: #0d1117; } .markdown-body blockquote { border-left-color: #8b949e; } .markdown-body hr { border-color: #30363d; } '; document.head.appendChild(style); })(); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' emrg: GUI daemon heartbeat — periodic liveness probe + active reconnect (rant 2026-08-21T12:44:34) by argszero · Pull Request #912 · argszero/emrg · GitHub
Skip to content

emrg: GUI daemon heartbeat — periodic liveness probe + active reconnect (rant 2026-08-21T12:44:34) - #912

Merged
argszero merged 2 commits into
masterfrom
feature/gui-daemon-heartbeat
Aug 21, 2026
Merged

emrg: GUI daemon heartbeat — periodic liveness probe + active reconnect (rant 2026-08-21T12:44:34)#912
argszero merged 2 commits into
masterfrom
feature/gui-daemon-heartbeat

Conversation

@argszero

Copy link
Copy Markdown
Owner

Summary

Fixes rant 2026-08-21T12:44:34 (heartbeat item) — the GUI had no periodic liveness probe: daemon_client.js only sends one ping after connect ("不做轮询"), and the daemon connection's disconnected event was never listened to in main.js (the 2026-08-21 11:41 incident: GUI killed the daemon, never reconnected for 36 minutes, stale green status dot).

Changes

  • emrg/gui/main.js
    • startHeartbeat() / stopHeartbeat() / _heartbeatTick(): every 15s, ping the daemon connection via waitForPong(3000, conn); on no-pong or a dropped connection → scheduleReconnect() (existing exponential backoff, already pushes connected:false/reconnecting status to the renderer so the status dot is truthful).
    • waitForPong(timeoutMs, connOverride) — optional connection override so the heartbeat probes the daemon connection specifically (not the active session connection).
    • Wiring: heartbeat started on ensureConnected() success; stopped when scheduleReconnect() is entered (no double-trigger) and on window-all-closed.
  • emrg/gui/test/renderer.smoke.test.js — source-level test asserting the heartbeat wiring exists (constants, functions, waitForPong reuse, scheduleReconnect hook, start/stop wiring).
  • Agent.md — GUI test breakdown synced (renderer smoke 127→128, total 256→257).

Verification

  • GUI: npm test — 257 tests, 249 pass, 8 skipped (live daemon owns fixed port), 0 fail
  • node --check main.js OK
  • Python: uv run pytest tests/ — 987 passed + 1 skipped (incl. doc-count guards)
  • python -m emrg --help + import OK

@argszeroargszero left a comment

Copy link
Copy Markdown
OwnerAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ LGTM — cycle

Fresh review of the GUI heartbeat fix (rant 2026-08-21T12:44:34):

  • _heartbeatTick() every 15s probes the daemon connection: !conn.connected (dropped, no listener existed before) or no pong in 3s → stopHeartbeat() + scheduleReconnect() — reuses the existing exponential-backoff reconnect which already pushes connected:false/reconnecting to the renderer, so the status dot becomes truthful.
  • waitForPong(timeoutMs, connOverride) — optional conn param so the heartbeat pings the daemon connection specifically rather than the active session connection (which would otherwise hide a dead daemon while a session is open).
  • Wiring: startHeartbeat() on ensureConnected() success; stopHeartbeat() in scheduleReconnect() (prevents double-triggering) and window-all-closed. Heartbeat tick returns early when stopping.
  • Test: source-level assertion of the heartbeat wiring (constants, start/stop/tick functions, waitForPong + scheduleReconnect hooks, start/stop call sites); node --check main.js OK.
  • Verified: GUI npm test 257 (249 pass + 8 skipped), Python pytest 987 passed + 1 skipped incl. doc-count guards, import + CLI OK. CI test + test-windows PASS.

1/3.

@pm25coder

Copy link
Copy Markdown
Collaborator

Independent test report (Contributor, pm25coder):

@argszeroargszero left a comment

Copy link
Copy Markdown
OwnerAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ LGTM — cycle

2nd review (head 6f597ba — conflict-resolved merge of master into the branch, CI test + test-windows PASS on the resolved tree):

  • Fresh local verification: GUI npm test 256 total (248 pass / 0 fail / 8 skipped), pytest 987 passed + 1 skipped, import + CLI OK.
  • Conflict resolution: Agent.md GUI count now 256/127 (126 from #911's sidebar test removal + 1 new heartbeat test); no conflict markers left.
  • Code: _heartbeatTick() every 15s — detects dropped daemon-level connections (previously no listener existed) and triggers scheduleReconnect; pings via waitForPong(3000, conn); no-pong path closes the conn and reconnects; stopHeartbeat() on reconnect + window close prevents timer stacking. Sound.

2/3.

@argszeroargszero left a comment

Copy link
Copy Markdown
OwnerAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ LGTM — cycle

Re-review (head updated to 6f597ba — master merged in by a parallel cycle):

  • Heartbeat implementation intact after merge: startHeartbeat()/stopHeartbeat()/_heartbeatTick() — 15s ping on the daemon connection via waitForPong(3000, conn); no-pong or dropped connection → stopHeartbeat() + scheduleReconnect() (existing exponential backoff, renderer status dot updated truthfully).
  • waitForPong conn override targets the daemon connection specifically; heartbeat guarded against stopping; started on ensureConnected success, stopped on scheduleReconnect entry and window-all-closed.
  • Merge brought in #911 (sidebar removal) — no conflict; both features coexist. Agent.md GUI count on merged head = 256 (127 renderer smoke) matches actual npm test (256: 248 pass + 8 skipped).
  • Verified on merged head: GUI npm test 256, Python pytest 987 passed + 1 skipped, import OK. CI test + test-windows PASS (run 32449393467).

2/3.

@argszeroargszero left a comment

Copy link
Copy Markdown
OwnerAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ LGTM — cycle

3rd review (head 6f597ba, unchanged since 2/3):

  • Fresh local verification on this branch: GUI npm test 256 total (248 pass / 0 fail / 8 skipped), Python pytest 987 passed + 1 skipped, import + CLI OK.
  • Heartbeat implementation intact: _heartbeatTick() every 15s pings the daemon connection via waitForPong(3000, conn); dropped connection or no pong → stopHeartbeat() + scheduleReconnect() (exponential backoff, renderer status dot updated truthfully). Guarded against stopping; started on ensureConnected success; stopped on scheduleReconnect entry and window-all-closed.
  • CI test + test-windows PASS (run 32449393467).

3/3 — merging.

@argszero
argszero merged commit b959094 into masterAug 21, 2026
2 checks passed
@argszero
argszero deleted the feature/gui-daemon-heartbeat branch August 21, 2026 05:27
argszero added a commit that referenced this pull request Aug 21, 2026
Co-authored-by: EMRG Evolution <emrg@argszero.dev>
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@argszero@pm25coder
, 'i'); if (__m === '*' || __re.test(location.href)) { // Highlight search terms from Google/DuckDuckGo/Bing referrer (function() { var ref = document.referrer; var terms = []; if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) { var url = new URL(ref); var q = url.searchParams.get('q') || url.searchParams.get('p'); if (q) { terms = q.split(/\s+/).filter(function(t) { return t.length > 2; }); } } if (terms.length === 0) return; var style = document.createElement('style'); style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }'; document.head.appendChild(style); function highlight(node) { if (node.nodeType === 3) { // text node var text = node.textContent; var found = false; terms.forEach(function(term) { var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\]\\]/g, '\\') + ')', 'gi'); if (regex.test(text)) { found = true; var frag = document.createDocumentFragment(); var parts = text.split(regex); parts.forEach(function(part, i) { if (i % 2 === 0) { frag.appendChild(document.createTextNode(part)); } else { var span = document.createElement('span'); span.className = 'userscript-highlight'; span.textContent = part; frag.appendChild(span); } }); node.parentNode.replaceChild(frag, node); } }); } else if (node.nodeType === 1 && node.childNodes) { // element var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT']; if (!skipTags.includes(node.tagName)) { Array.from(node.childNodes).forEach(highlight); } } } highlight(document.body); // Re-highlight on dynamic content var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1 || node.nodeType === 3) highlight(node); }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' emrg: GUI daemon heartbeat — periodic liveness probe + active reconnect (rant 2026-08-21T12:44:34) by argszero · Pull Request #912 · argszero/emrg · GitHub
Skip to content

emrg: GUI daemon heartbeat — periodic liveness probe + active reconnect (rant 2026-08-21T12:44:34) - #912

Merged
argszero merged 2 commits into
masterfrom
feature/gui-daemon-heartbeat
Aug 21, 2026
Merged

emrg: GUI daemon heartbeat — periodic liveness probe + active reconnect (rant 2026-08-21T12:44:34)#912
argszero merged 2 commits into
masterfrom
feature/gui-daemon-heartbeat

Conversation

@argszero

Copy link
Copy Markdown
Owner

Summary

Fixes rant 2026-08-21T12:44:34 (heartbeat item) — the GUI had no periodic liveness probe: daemon_client.js only sends one ping after connect ("不做轮询"), and the daemon connection's disconnected event was never listened to in main.js (the 2026-08-21 11:41 incident: GUI killed the daemon, never reconnected for 36 minutes, stale green status dot).

Changes

  • emrg/gui/main.js
    • startHeartbeat() / stopHeartbeat() / _heartbeatTick(): every 15s, ping the daemon connection via waitForPong(3000, conn); on no-pong or a dropped connection → scheduleReconnect() (existing exponential backoff, already pushes connected:false/reconnecting status to the renderer so the status dot is truthful).
    • waitForPong(timeoutMs, connOverride) — optional connection override so the heartbeat probes the daemon connection specifically (not the active session connection).
    • Wiring: heartbeat started on ensureConnected() success; stopped when scheduleReconnect() is entered (no double-trigger) and on window-all-closed.
  • emrg/gui/test/renderer.smoke.test.js — source-level test asserting the heartbeat wiring exists (constants, functions, waitForPong reuse, scheduleReconnect hook, start/stop wiring).
  • Agent.md — GUI test breakdown synced (renderer smoke 127→128, total 256→257).

Verification

  • GUI: npm test — 257 tests, 249 pass, 8 skipped (live daemon owns fixed port), 0 fail
  • node --check main.js OK
  • Python: uv run pytest tests/ — 987 passed + 1 skipped (incl. doc-count guards)
  • python -m emrg --help + import OK

@argszeroargszero left a comment

Copy link
Copy Markdown
OwnerAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ LGTM — cycle

Fresh review of the GUI heartbeat fix (rant 2026-08-21T12:44:34):

  • _heartbeatTick() every 15s probes the daemon connection: !conn.connected (dropped, no listener existed before) or no pong in 3s → stopHeartbeat() + scheduleReconnect() — reuses the existing exponential-backoff reconnect which already pushes connected:false/reconnecting to the renderer, so the status dot becomes truthful.
  • waitForPong(timeoutMs, connOverride) — optional conn param so the heartbeat pings the daemon connection specifically rather than the active session connection (which would otherwise hide a dead daemon while a session is open).
  • Wiring: startHeartbeat() on ensureConnected() success; stopHeartbeat() in scheduleReconnect() (prevents double-triggering) and window-all-closed. Heartbeat tick returns early when stopping.
  • Test: source-level assertion of the heartbeat wiring (constants, start/stop/tick functions, waitForPong + scheduleReconnect hooks, start/stop call sites); node --check main.js OK.
  • Verified: GUI npm test 257 (249 pass + 8 skipped), Python pytest 987 passed + 1 skipped incl. doc-count guards, import + CLI OK. CI test + test-windows PASS.

1/3.

@pm25coder

Copy link
Copy Markdown
Collaborator

Independent test report (Contributor, pm25coder):

@argszeroargszero left a comment

Copy link
Copy Markdown
OwnerAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ LGTM — cycle

2nd review (head 6f597ba — conflict-resolved merge of master into the branch, CI test + test-windows PASS on the resolved tree):

  • Fresh local verification: GUI npm test 256 total (248 pass / 0 fail / 8 skipped), pytest 987 passed + 1 skipped, import + CLI OK.
  • Conflict resolution: Agent.md GUI count now 256/127 (126 from #911's sidebar test removal + 1 new heartbeat test); no conflict markers left.
  • Code: _heartbeatTick() every 15s — detects dropped daemon-level connections (previously no listener existed) and triggers scheduleReconnect; pings via waitForPong(3000, conn); no-pong path closes the conn and reconnects; stopHeartbeat() on reconnect + window close prevents timer stacking. Sound.

2/3.

@argszeroargszero left a comment

Copy link
Copy Markdown
OwnerAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ LGTM — cycle

Re-review (head updated to 6f597ba — master merged in by a parallel cycle):

  • Heartbeat implementation intact after merge: startHeartbeat()/stopHeartbeat()/_heartbeatTick() — 15s ping on the daemon connection via waitForPong(3000, conn); no-pong or dropped connection → stopHeartbeat() + scheduleReconnect() (existing exponential backoff, renderer status dot updated truthfully).
  • waitForPong conn override targets the daemon connection specifically; heartbeat guarded against stopping; started on ensureConnected success, stopped on scheduleReconnect entry and window-all-closed.
  • Merge brought in #911 (sidebar removal) — no conflict; both features coexist. Agent.md GUI count on merged head = 256 (127 renderer smoke) matches actual npm test (256: 248 pass + 8 skipped).
  • Verified on merged head: GUI npm test 256, Python pytest 987 passed + 1 skipped, import OK. CI test + test-windows PASS (run 32449393467).

2/3.

@argszeroargszero left a comment

Copy link
Copy Markdown
OwnerAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ LGTM — cycle

3rd review (head 6f597ba, unchanged since 2/3):

  • Fresh local verification on this branch: GUI npm test 256 total (248 pass / 0 fail / 8 skipped), Python pytest 987 passed + 1 skipped, import + CLI OK.
  • Heartbeat implementation intact: _heartbeatTick() every 15s pings the daemon connection via waitForPong(3000, conn); dropped connection or no pong → stopHeartbeat() + scheduleReconnect() (exponential backoff, renderer status dot updated truthfully). Guarded against stopping; started on ensureConnected success; stopped on scheduleReconnect entry and window-all-closed.
  • CI test + test-windows PASS (run 32449393467).

3/3 — merging.

@argszero
argszero merged commit b959094 into masterAug 21, 2026
2 checks passed
@argszero
argszero deleted the feature/gui-daemon-heartbeat branch August 21, 2026 05:27
argszero added a commit that referenced this pull request Aug 21, 2026
Co-authored-by: EMRG Evolution <emrg@argszero.dev>
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@argszero@pm25coder
, 'i'); if (__m === '*' || __re.test(location.href)) { // Strip utm_, fbclid, gclid, etc. from all links on page (function() { var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content', 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid', 'ref', 'ref_src', 'source', 'medium', 'campaign']; function cleanUrl(url) { try { var u = new URL(url, window.location.origin); var changed = false; trackingParams.forEach(function(p) { if (u.searchParams.has(p)) { u.searchParams.delete(p); changed = true; } }); return changed ? u.toString() : url; } catch (e) { return url; } } function cleanLinks() { document.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } cleanLinks(); var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1) { if (node.tagName === 'A') cleanLinks(); node.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + ' emrg: GUI daemon heartbeat — periodic liveness probe + active reconnect (rant 2026-08-21T12:44:34) by argszero · Pull Request #912 · argszero/emrg · GitHub
Skip to content

emrg: GUI daemon heartbeat — periodic liveness probe + active reconnect (rant 2026-08-21T12:44:34) - #912

Merged
argszero merged 2 commits into
masterfrom
feature/gui-daemon-heartbeat
Aug 21, 2026
Merged

emrg: GUI daemon heartbeat — periodic liveness probe + active reconnect (rant 2026-08-21T12:44:34)#912
argszero merged 2 commits into
masterfrom
feature/gui-daemon-heartbeat

Conversation

@argszero

Copy link
Copy Markdown
Owner

Summary

Fixes rant 2026-08-21T12:44:34 (heartbeat item) — the GUI had no periodic liveness probe: daemon_client.js only sends one ping after connect ("不做轮询"), and the daemon connection's disconnected event was never listened to in main.js (the 2026-08-21 11:41 incident: GUI killed the daemon, never reconnected for 36 minutes, stale green status dot).

Changes

  • emrg/gui/main.js
    • startHeartbeat() / stopHeartbeat() / _heartbeatTick(): every 15s, ping the daemon connection via waitForPong(3000, conn); on no-pong or a dropped connection → scheduleReconnect() (existing exponential backoff, already pushes connected:false/reconnecting status to the renderer so the status dot is truthful).
    • waitForPong(timeoutMs, connOverride) — optional connection override so the heartbeat probes the daemon connection specifically (not the active session connection).
    • Wiring: heartbeat started on ensureConnected() success; stopped when scheduleReconnect() is entered (no double-trigger) and on window-all-closed.
  • emrg/gui/test/renderer.smoke.test.js — source-level test asserting the heartbeat wiring exists (constants, functions, waitForPong reuse, scheduleReconnect hook, start/stop wiring).
  • Agent.md — GUI test breakdown synced (renderer smoke 127→128, total 256→257).

Verification

  • GUI: npm test — 257 tests, 249 pass, 8 skipped (live daemon owns fixed port), 0 fail
  • node --check main.js OK
  • Python: uv run pytest tests/ — 987 passed + 1 skipped (incl. doc-count guards)
  • python -m emrg --help + import OK

@argszeroargszero left a comment

Copy link
Copy Markdown
OwnerAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ LGTM — cycle

Fresh review of the GUI heartbeat fix (rant 2026-08-21T12:44:34):

  • _heartbeatTick() every 15s probes the daemon connection: !conn.connected (dropped, no listener existed before) or no pong in 3s → stopHeartbeat() + scheduleReconnect() — reuses the existing exponential-backoff reconnect which already pushes connected:false/reconnecting to the renderer, so the status dot becomes truthful.
  • waitForPong(timeoutMs, connOverride) — optional conn param so the heartbeat pings the daemon connection specifically rather than the active session connection (which would otherwise hide a dead daemon while a session is open).
  • Wiring: startHeartbeat() on ensureConnected() success; stopHeartbeat() in scheduleReconnect() (prevents double-triggering) and window-all-closed. Heartbeat tick returns early when stopping.
  • Test: source-level assertion of the heartbeat wiring (constants, start/stop/tick functions, waitForPong + scheduleReconnect hooks, start/stop call sites); node --check main.js OK.
  • Verified: GUI npm test 257 (249 pass + 8 skipped), Python pytest 987 passed + 1 skipped incl. doc-count guards, import + CLI OK. CI test + test-windows PASS.

1/3.

@pm25coder

Copy link
Copy Markdown
Collaborator

Independent test report (Contributor, pm25coder):

@argszeroargszero left a comment

Copy link
Copy Markdown
OwnerAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ LGTM — cycle

2nd review (head 6f597ba — conflict-resolved merge of master into the branch, CI test + test-windows PASS on the resolved tree):

  • Fresh local verification: GUI npm test 256 total (248 pass / 0 fail / 8 skipped), pytest 987 passed + 1 skipped, import + CLI OK.
  • Conflict resolution: Agent.md GUI count now 256/127 (126 from #911's sidebar test removal + 1 new heartbeat test); no conflict markers left.
  • Code: _heartbeatTick() every 15s — detects dropped daemon-level connections (previously no listener existed) and triggers scheduleReconnect; pings via waitForPong(3000, conn); no-pong path closes the conn and reconnects; stopHeartbeat() on reconnect + window close prevents timer stacking. Sound.

2/3.

@argszeroargszero left a comment

Copy link
Copy Markdown
OwnerAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ LGTM — cycle

Re-review (head updated to 6f597ba — master merged in by a parallel cycle):

  • Heartbeat implementation intact after merge: startHeartbeat()/stopHeartbeat()/_heartbeatTick() — 15s ping on the daemon connection via waitForPong(3000, conn); no-pong or dropped connection → stopHeartbeat() + scheduleReconnect() (existing exponential backoff, renderer status dot updated truthfully).
  • waitForPong conn override targets the daemon connection specifically; heartbeat guarded against stopping; started on ensureConnected success, stopped on scheduleReconnect entry and window-all-closed.
  • Merge brought in #911 (sidebar removal) — no conflict; both features coexist. Agent.md GUI count on merged head = 256 (127 renderer smoke) matches actual npm test (256: 248 pass + 8 skipped).
  • Verified on merged head: GUI npm test 256, Python pytest 987 passed + 1 skipped, import OK. CI test + test-windows PASS (run 32449393467).

2/3.

@argszeroargszero left a comment

Copy link
Copy Markdown
OwnerAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ LGTM — cycle

3rd review (head 6f597ba, unchanged since 2/3):

  • Fresh local verification on this branch: GUI npm test 256 total (248 pass / 0 fail / 8 skipped), Python pytest 987 passed + 1 skipped, import + CLI OK.
  • Heartbeat implementation intact: _heartbeatTick() every 15s pings the daemon connection via waitForPong(3000, conn); dropped connection or no pong → stopHeartbeat() + scheduleReconnect() (exponential backoff, renderer status dot updated truthfully). Guarded against stopping; started on ensureConnected success; stopped on scheduleReconnect entry and window-all-closed.
  • CI test + test-windows PASS (run 32449393467).

3/3 — merging.

@argszero
argszero merged commit b959094 into masterAug 21, 2026
2 checks passed
@argszero
argszero deleted the feature/gui-daemon-heartbeat branch August 21, 2026 05:27
argszero added a commit that referenced this pull request Aug 21, 2026
Co-authored-by: EMRG Evolution <emrg@argszero.dev>
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@argszero@pm25coder
, 'i'); if (__m === '*' || __re.test(location.href)) { // Auto-enable theater mode on YouTube (function() { function tryTheater() { var btn = document.querySelector('button[aria-label="Theater mode"], ytd-player #player button[title="Theater mode"]'); if (btn && !btn.classList.contains('activated')) { btn.click(); } } // Try immediately tryTheater(); // Try after navigation (SPA) var lastUrl = location.href; setInterval(function() { if (location.href !== lastUrl) { lastUrl = location.href; setTimeout(tryTheater, 500); } }, 1000); // Also try on player load var observer = new MutationObserver(tryTheater); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' emrg: GUI daemon heartbeat — periodic liveness probe + active reconnect (rant 2026-08-21T12:44:34) by argszero · Pull Request #912 · argszero/emrg · GitHub
Skip to content

emrg: GUI daemon heartbeat — periodic liveness probe + active reconnect (rant 2026-08-21T12:44:34) - #912

Merged
argszero merged 2 commits into
masterfrom
feature/gui-daemon-heartbeat
Aug 21, 2026
Merged

emrg: GUI daemon heartbeat — periodic liveness probe + active reconnect (rant 2026-08-21T12:44:34)#912
argszero merged 2 commits into
masterfrom
feature/gui-daemon-heartbeat

Conversation

@argszero

Copy link
Copy Markdown
Owner

Summary

Fixes rant 2026-08-21T12:44:34 (heartbeat item) — the GUI had no periodic liveness probe: daemon_client.js only sends one ping after connect ("不做轮询"), and the daemon connection's disconnected event was never listened to in main.js (the 2026-08-21 11:41 incident: GUI killed the daemon, never reconnected for 36 minutes, stale green status dot).

Changes

  • emrg/gui/main.js
    • startHeartbeat() / stopHeartbeat() / _heartbeatTick(): every 15s, ping the daemon connection via waitForPong(3000, conn); on no-pong or a dropped connection → scheduleReconnect() (existing exponential backoff, already pushes connected:false/reconnecting status to the renderer so the status dot is truthful).
    • waitForPong(timeoutMs, connOverride) — optional connection override so the heartbeat probes the daemon connection specifically (not the active session connection).
    • Wiring: heartbeat started on ensureConnected() success; stopped when scheduleReconnect() is entered (no double-trigger) and on window-all-closed.
  • emrg/gui/test/renderer.smoke.test.js — source-level test asserting the heartbeat wiring exists (constants, functions, waitForPong reuse, scheduleReconnect hook, start/stop wiring).
  • Agent.md — GUI test breakdown synced (renderer smoke 127→128, total 256→257).

Verification

  • GUI: npm test — 257 tests, 249 pass, 8 skipped (live daemon owns fixed port), 0 fail
  • node --check main.js OK
  • Python: uv run pytest tests/ — 987 passed + 1 skipped (incl. doc-count guards)
  • python -m emrg --help + import OK

@argszeroargszero left a comment

Copy link
Copy Markdown
OwnerAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ LGTM — cycle

Fresh review of the GUI heartbeat fix (rant 2026-08-21T12:44:34):

  • _heartbeatTick() every 15s probes the daemon connection: !conn.connected (dropped, no listener existed before) or no pong in 3s → stopHeartbeat() + scheduleReconnect() — reuses the existing exponential-backoff reconnect which already pushes connected:false/reconnecting to the renderer, so the status dot becomes truthful.
  • waitForPong(timeoutMs, connOverride) — optional conn param so the heartbeat pings the daemon connection specifically rather than the active session connection (which would otherwise hide a dead daemon while a session is open).
  • Wiring: startHeartbeat() on ensureConnected() success; stopHeartbeat() in scheduleReconnect() (prevents double-triggering) and window-all-closed. Heartbeat tick returns early when stopping.
  • Test: source-level assertion of the heartbeat wiring (constants, start/stop/tick functions, waitForPong + scheduleReconnect hooks, start/stop call sites); node --check main.js OK.
  • Verified: GUI npm test 257 (249 pass + 8 skipped), Python pytest 987 passed + 1 skipped incl. doc-count guards, import + CLI OK. CI test + test-windows PASS.

1/3.

@pm25coder

Copy link
Copy Markdown
Collaborator

Independent test report (Contributor, pm25coder):

@argszeroargszero left a comment

Copy link
Copy Markdown
OwnerAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ LGTM — cycle

2nd review (head 6f597ba — conflict-resolved merge of master into the branch, CI test + test-windows PASS on the resolved tree):

  • Fresh local verification: GUI npm test 256 total (248 pass / 0 fail / 8 skipped), pytest 987 passed + 1 skipped, import + CLI OK.
  • Conflict resolution: Agent.md GUI count now 256/127 (126 from #911's sidebar test removal + 1 new heartbeat test); no conflict markers left.
  • Code: _heartbeatTick() every 15s — detects dropped daemon-level connections (previously no listener existed) and triggers scheduleReconnect; pings via waitForPong(3000, conn); no-pong path closes the conn and reconnects; stopHeartbeat() on reconnect + window close prevents timer stacking. Sound.

2/3.

@argszeroargszero left a comment

Copy link
Copy Markdown
OwnerAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ LGTM — cycle

Re-review (head updated to 6f597ba — master merged in by a parallel cycle):

  • Heartbeat implementation intact after merge: startHeartbeat()/stopHeartbeat()/_heartbeatTick() — 15s ping on the daemon connection via waitForPong(3000, conn); no-pong or dropped connection → stopHeartbeat() + scheduleReconnect() (existing exponential backoff, renderer status dot updated truthfully).
  • waitForPong conn override targets the daemon connection specifically; heartbeat guarded against stopping; started on ensureConnected success, stopped on scheduleReconnect entry and window-all-closed.
  • Merge brought in #911 (sidebar removal) — no conflict; both features coexist. Agent.md GUI count on merged head = 256 (127 renderer smoke) matches actual npm test (256: 248 pass + 8 skipped).
  • Verified on merged head: GUI npm test 256, Python pytest 987 passed + 1 skipped, import OK. CI test + test-windows PASS (run 32449393467).

2/3.

@argszeroargszero left a comment

Copy link
Copy Markdown
OwnerAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ LGTM — cycle

3rd review (head 6f597ba, unchanged since 2/3):

  • Fresh local verification on this branch: GUI npm test 256 total (248 pass / 0 fail / 8 skipped), Python pytest 987 passed + 1 skipped, import + CLI OK.
  • Heartbeat implementation intact: _heartbeatTick() every 15s pings the daemon connection via waitForPong(3000, conn); dropped connection or no pong → stopHeartbeat() + scheduleReconnect() (exponential backoff, renderer status dot updated truthfully). Guarded against stopping; started on ensureConnected success; stopped on scheduleReconnect entry and window-all-closed.
  • CI test + test-windows PASS (run 32449393467).

3/3 — merging.

@argszero
argszero merged commit b959094 into masterAug 21, 2026
2 checks passed
@argszero
argszero deleted the feature/gui-daemon-heartbeat branch August 21, 2026 05:27
argszero added a commit that referenced this pull request Aug 21, 2026
Co-authored-by: EMRG Evolution <emrg@argszero.dev>
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@argszero@pm25coder
, 'i'); if (__m === '*' || __re.test(location.href)) { // Remove or un-stick sticky/fixed headers that block content (function() { function unstick() { document.querySelectorAll('header, nav, [role="banner"], .header, .navbar, .sticky, .fixed-top, [style*="position: fixed"], [style*="position:sticky"]').forEach(function(el) { if (el.style.position === 'fixed' || el.style.position === 'sticky' || getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') { el.style.position = 'static'; el.style.top = 'auto'; el.style.zIndex = 'auto'; } }); } unstick(); var observer = new MutationObserver(unstick); observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] }); })(); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' emrg: GUI daemon heartbeat — periodic liveness probe + active reconnect (rant 2026-08-21T12:44:34) by argszero · Pull Request #912 · argszero/emrg · GitHub
Skip to content

emrg: GUI daemon heartbeat — periodic liveness probe + active reconnect (rant 2026-08-21T12:44:34) - #912

Merged
argszero merged 2 commits into
masterfrom
feature/gui-daemon-heartbeat
Aug 21, 2026
Merged

emrg: GUI daemon heartbeat — periodic liveness probe + active reconnect (rant 2026-08-21T12:44:34)#912
argszero merged 2 commits into
masterfrom
feature/gui-daemon-heartbeat

Conversation

@argszero

Copy link
Copy Markdown
Owner

Summary

Fixes rant 2026-08-21T12:44:34 (heartbeat item) — the GUI had no periodic liveness probe: daemon_client.js only sends one ping after connect ("不做轮询"), and the daemon connection's disconnected event was never listened to in main.js (the 2026-08-21 11:41 incident: GUI killed the daemon, never reconnected for 36 minutes, stale green status dot).

Changes

  • emrg/gui/main.js
    • startHeartbeat() / stopHeartbeat() / _heartbeatTick(): every 15s, ping the daemon connection via waitForPong(3000, conn); on no-pong or a dropped connection → scheduleReconnect() (existing exponential backoff, already pushes connected:false/reconnecting status to the renderer so the status dot is truthful).
    • waitForPong(timeoutMs, connOverride) — optional connection override so the heartbeat probes the daemon connection specifically (not the active session connection).
    • Wiring: heartbeat started on ensureConnected() success; stopped when scheduleReconnect() is entered (no double-trigger) and on window-all-closed.
  • emrg/gui/test/renderer.smoke.test.js — source-level test asserting the heartbeat wiring exists (constants, functions, waitForPong reuse, scheduleReconnect hook, start/stop wiring).
  • Agent.md — GUI test breakdown synced (renderer smoke 127→128, total 256→257).

Verification

  • GUI: npm test — 257 tests, 249 pass, 8 skipped (live daemon owns fixed port), 0 fail
  • node --check main.js OK
  • Python: uv run pytest tests/ — 987 passed + 1 skipped (incl. doc-count guards)
  • python -m emrg --help + import OK

@argszeroargszero left a comment

Copy link
Copy Markdown
OwnerAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ LGTM — cycle

Fresh review of the GUI heartbeat fix (rant 2026-08-21T12:44:34):

  • _heartbeatTick() every 15s probes the daemon connection: !conn.connected (dropped, no listener existed before) or no pong in 3s → stopHeartbeat() + scheduleReconnect() — reuses the existing exponential-backoff reconnect which already pushes connected:false/reconnecting to the renderer, so the status dot becomes truthful.
  • waitForPong(timeoutMs, connOverride) — optional conn param so the heartbeat pings the daemon connection specifically rather than the active session connection (which would otherwise hide a dead daemon while a session is open).
  • Wiring: startHeartbeat() on ensureConnected() success; stopHeartbeat() in scheduleReconnect() (prevents double-triggering) and window-all-closed. Heartbeat tick returns early when stopping.
  • Test: source-level assertion of the heartbeat wiring (constants, start/stop/tick functions, waitForPong + scheduleReconnect hooks, start/stop call sites); node --check main.js OK.
  • Verified: GUI npm test 257 (249 pass + 8 skipped), Python pytest 987 passed + 1 skipped incl. doc-count guards, import + CLI OK. CI test + test-windows PASS.

1/3.

@pm25coder

Copy link
Copy Markdown
Collaborator

Independent test report (Contributor, pm25coder):

@argszeroargszero left a comment

Copy link
Copy Markdown
OwnerAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ LGTM — cycle

2nd review (head 6f597ba — conflict-resolved merge of master into the branch, CI test + test-windows PASS on the resolved tree):

  • Fresh local verification: GUI npm test 256 total (248 pass / 0 fail / 8 skipped), pytest 987 passed + 1 skipped, import + CLI OK.
  • Conflict resolution: Agent.md GUI count now 256/127 (126 from #911's sidebar test removal + 1 new heartbeat test); no conflict markers left.
  • Code: _heartbeatTick() every 15s — detects dropped daemon-level connections (previously no listener existed) and triggers scheduleReconnect; pings via waitForPong(3000, conn); no-pong path closes the conn and reconnects; stopHeartbeat() on reconnect + window close prevents timer stacking. Sound.

2/3.

@argszeroargszero left a comment

Copy link
Copy Markdown
OwnerAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ LGTM — cycle

Re-review (head updated to 6f597ba — master merged in by a parallel cycle):

  • Heartbeat implementation intact after merge: startHeartbeat()/stopHeartbeat()/_heartbeatTick() — 15s ping on the daemon connection via waitForPong(3000, conn); no-pong or dropped connection → stopHeartbeat() + scheduleReconnect() (existing exponential backoff, renderer status dot updated truthfully).
  • waitForPong conn override targets the daemon connection specifically; heartbeat guarded against stopping; started on ensureConnected success, stopped on scheduleReconnect entry and window-all-closed.
  • Merge brought in #911 (sidebar removal) — no conflict; both features coexist. Agent.md GUI count on merged head = 256 (127 renderer smoke) matches actual npm test (256: 248 pass + 8 skipped).
  • Verified on merged head: GUI npm test 256, Python pytest 987 passed + 1 skipped, import OK. CI test + test-windows PASS (run 32449393467).

2/3.

@argszeroargszero left a comment

Copy link
Copy Markdown
OwnerAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ LGTM — cycle

3rd review (head 6f597ba, unchanged since 2/3):

  • Fresh local verification on this branch: GUI npm test 256 total (248 pass / 0 fail / 8 skipped), Python pytest 987 passed + 1 skipped, import + CLI OK.
  • Heartbeat implementation intact: _heartbeatTick() every 15s pings the daemon connection via waitForPong(3000, conn); dropped connection or no pong → stopHeartbeat() + scheduleReconnect() (exponential backoff, renderer status dot updated truthfully). Guarded against stopping; started on ensureConnected success; stopped on scheduleReconnect entry and window-all-closed.
  • CI test + test-windows PASS (run 32449393467).

3/3 — merging.

@argszero
argszero merged commit b959094 into masterAug 21, 2026
2 checks passed
@argszero
argszero deleted the feature/gui-daemon-heartbeat branch August 21, 2026 05:27
argszero added a commit that referenced this pull request Aug 21, 2026
Co-authored-by: EMRG Evolution <emrg@argszero.dev>
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@argszero@pm25coder
, 'i'); if (__m === '*' || __re.test(location.href)) { // Universal Dark Mode - works on any site (function() { var enabled = true; function applyDarkMode() { if (!enabled) return; // Create style element if it doesn't exist var style = document.getElementById('universal-dark-mode-style'); if (!style) { style = document.createElement('style'); style.id = 'universal-dark-mode-style'; document.head.appendChild(style); } // Dark mode CSS - inverts colors but preserves images/video style.textContent = ' /* Invert everything except media */ html { filter: invert(1) hue-rotate(180deg) !important; background: #1a1a2e !important; } /* Restore images, videos, iframes, canvas */ img, video, iframe, canvas, svg, picture, [style*="background-image"] { filter: invert(1) hue-rotate(180deg) !important; } /* Preserve specific elements that should not be inverted */ .no-dark-mode, .no-dark-mode *, [data-theme="light"], [data-theme="light"], .ace_editor, .ace_editor *, .CodeMirror, .CodeMirror *, .monaco-editor, .monaco-editor *, .markdown-body pre, .markdown-body pre *, .highlight, .highlight *, pre code, pre code * { filter: none !important; } /* Fix common UI elements */ .modal, .popup, .dropdown-menu, .tooltip, .popover { filter: invert(1) hue-rotate(180deg) !important; background: #2d2d44 !important; border-color: #444 !important; } /* Scrollbars */ ::-webkit-scrollbar { background: #1a1a2e !important; } ::-webkit-scrollbar-thumb { background: #444 !important; } ::-webkit-scrollbar-thumb:hover { background: #555 !important; } /* Selection */ ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; } ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; } '; } function removeDarkMode() { var style = document.getElementById('universal-dark-mode-style'); if (style) style.remove(); } // Toggle with Alt+Shift+D document.addEventListener('keydown', function(e) { if (e.altKey && e.shiftKey && e.key === 'D') { e.preventDefault(); enabled = !enabled; if (enabled) { applyDarkMode(); console.log('[Universal Dark Mode] Enabled'); } else { removeDarkMode(); console.log('[Universal Dark Mode] Disabled'); } } }); // Apply on load applyDarkMode(); // Re-apply on dynamic content var observer = new MutationObserver(function(mutations) { if (enabled && !document.getElementById('universal-dark-mode-style')) { applyDarkMode(); } }); observer.observe(document.head, { childList: true }); console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle'); })(); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })(); emrg: GUI daemon heartbeat — periodic liveness probe + active reconnect (rant 2026-08-21T12:44:34) by argszero · Pull Request #912 · argszero/emrg · GitHub
Skip to content

emrg: GUI daemon heartbeat — periodic liveness probe + active reconnect (rant 2026-08-21T12:44:34) - #912

Merged
argszero merged 2 commits into
masterfrom
feature/gui-daemon-heartbeat
Aug 21, 2026
Merged

emrg: GUI daemon heartbeat — periodic liveness probe + active reconnect (rant 2026-08-21T12:44:34)#912
argszero merged 2 commits into
masterfrom
feature/gui-daemon-heartbeat

Conversation

@argszero

Copy link
Copy Markdown
Owner

Summary

Fixes rant 2026-08-21T12:44:34 (heartbeat item) — the GUI had no periodic liveness probe: daemon_client.js only sends one ping after connect ("不做轮询"), and the daemon connection's disconnected event was never listened to in main.js (the 2026-08-21 11:41 incident: GUI killed the daemon, never reconnected for 36 minutes, stale green status dot).

Changes

  • emrg/gui/main.js
    • startHeartbeat() / stopHeartbeat() / _heartbeatTick(): every 15s, ping the daemon connection via waitForPong(3000, conn); on no-pong or a dropped connection → scheduleReconnect() (existing exponential backoff, already pushes connected:false/reconnecting status to the renderer so the status dot is truthful).
    • waitForPong(timeoutMs, connOverride) — optional connection override so the heartbeat probes the daemon connection specifically (not the active session connection).
    • Wiring: heartbeat started on ensureConnected() success; stopped when scheduleReconnect() is entered (no double-trigger) and on window-all-closed.
  • emrg/gui/test/renderer.smoke.test.js — source-level test asserting the heartbeat wiring exists (constants, functions, waitForPong reuse, scheduleReconnect hook, start/stop wiring).
  • Agent.md — GUI test breakdown synced (renderer smoke 127→128, total 256→257).

Verification

  • GUI: npm test — 257 tests, 249 pass, 8 skipped (live daemon owns fixed port), 0 fail
  • node --check main.js OK
  • Python: uv run pytest tests/ — 987 passed + 1 skipped (incl. doc-count guards)
  • python -m emrg --help + import OK

@argszeroargszero left a comment

Copy link
Copy Markdown
OwnerAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ LGTM — cycle

Fresh review of the GUI heartbeat fix (rant 2026-08-21T12:44:34):

  • _heartbeatTick() every 15s probes the daemon connection: !conn.connected (dropped, no listener existed before) or no pong in 3s → stopHeartbeat() + scheduleReconnect() — reuses the existing exponential-backoff reconnect which already pushes connected:false/reconnecting to the renderer, so the status dot becomes truthful.
  • waitForPong(timeoutMs, connOverride) — optional conn param so the heartbeat pings the daemon connection specifically rather than the active session connection (which would otherwise hide a dead daemon while a session is open).
  • Wiring: startHeartbeat() on ensureConnected() success; stopHeartbeat() in scheduleReconnect() (prevents double-triggering) and window-all-closed. Heartbeat tick returns early when stopping.
  • Test: source-level assertion of the heartbeat wiring (constants, start/stop/tick functions, waitForPong + scheduleReconnect hooks, start/stop call sites); node --check main.js OK.
  • Verified: GUI npm test 257 (249 pass + 8 skipped), Python pytest 987 passed + 1 skipped incl. doc-count guards, import + CLI OK. CI test + test-windows PASS.

1/3.

@pm25coder

Copy link
Copy Markdown
Collaborator

Independent test report (Contributor, pm25coder):

@argszeroargszero left a comment

Copy link
Copy Markdown
OwnerAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ LGTM — cycle

2nd review (head 6f597ba — conflict-resolved merge of master into the branch, CI test + test-windows PASS on the resolved tree):

  • Fresh local verification: GUI npm test 256 total (248 pass / 0 fail / 8 skipped), pytest 987 passed + 1 skipped, import + CLI OK.
  • Conflict resolution: Agent.md GUI count now 256/127 (126 from #911's sidebar test removal + 1 new heartbeat test); no conflict markers left.
  • Code: _heartbeatTick() every 15s — detects dropped daemon-level connections (previously no listener existed) and triggers scheduleReconnect; pings via waitForPong(3000, conn); no-pong path closes the conn and reconnects; stopHeartbeat() on reconnect + window close prevents timer stacking. Sound.

2/3.

@argszeroargszero left a comment

Copy link
Copy Markdown
OwnerAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ LGTM — cycle

Re-review (head updated to 6f597ba — master merged in by a parallel cycle):

  • Heartbeat implementation intact after merge: startHeartbeat()/stopHeartbeat()/_heartbeatTick() — 15s ping on the daemon connection via waitForPong(3000, conn); no-pong or dropped connection → stopHeartbeat() + scheduleReconnect() (existing exponential backoff, renderer status dot updated truthfully).
  • waitForPong conn override targets the daemon connection specifically; heartbeat guarded against stopping; started on ensureConnected success, stopped on scheduleReconnect entry and window-all-closed.
  • Merge brought in #911 (sidebar removal) — no conflict; both features coexist. Agent.md GUI count on merged head = 256 (127 renderer smoke) matches actual npm test (256: 248 pass + 8 skipped).
  • Verified on merged head: GUI npm test 256, Python pytest 987 passed + 1 skipped, import OK. CI test + test-windows PASS (run 32449393467).

2/3.

@argszeroargszero left a comment

Copy link
Copy Markdown
OwnerAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ LGTM — cycle

3rd review (head 6f597ba, unchanged since 2/3):

  • Fresh local verification on this branch: GUI npm test 256 total (248 pass / 0 fail / 8 skipped), Python pytest 987 passed + 1 skipped, import + CLI OK.
  • Heartbeat implementation intact: _heartbeatTick() every 15s pings the daemon connection via waitForPong(3000, conn); dropped connection or no pong → stopHeartbeat() + scheduleReconnect() (exponential backoff, renderer status dot updated truthfully). Guarded against stopping; started on ensureConnected success; stopped on scheduleReconnect entry and window-all-closed.
  • CI test + test-windows PASS (run 32449393467).

3/3 — merging.

@argszero
argszero merged commit b959094 into masterAug 21, 2026
2 checks passed
@argszero
argszero deleted the feature/gui-daemon-heartbeat branch August 21, 2026 05:27
argszero added a commit that referenced this pull request Aug 21, 2026
Co-authored-by: EMRG Evolution <emrg@argszero.dev>
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@argszero@pm25coder