Skip to content

Replace a sidecar still running superseded code - #20

Merged
Castrozan merged 1 commit into
mainfrom
sidecar-stale-code-replacement
Aug 25, 2026
Merged

Replace a sidecar still running superseded code#20
Castrozan merged 1 commit into
mainfrom
sidecar-stale-code-replacement

Conversation

@Castrozan

Copy link
Copy Markdown
Owner

What was wrong

reconcile_one_sidecar_process asked pgrep for the sidecar's match pattern and, finding anything alive, returned without spawning. The pattern deliberately carries no store path, so a bridge launched from an older generation of bridge.py matches it exactly as well as the current one does.

A rebuild could therefore never put new bridge code on a running agent. Live evidence on chise: monster's bridge (pid 4193780, /nix/store/fx1wv2ds…-bridge.py) has run for two days across system generations 1618 to 1621. The media fix shipped in v0.12.5 would never have reached it.

What changed

spawn_sidecar_process records the command it launched beside the sidecar log (<log>.spawned-command). reconcile_one_sidecar_process compares that record to the desired command; on a mismatch it terminates every live process, waits for each to exit, then spawns the replacement.

The command string covers everything that can change: the bridge script's store path, the python environment, the one-shot turn command, the workspace and state directories.

A sidecar with no record at all counts as superseded, so the first reconcile after this change refreshes every bridge once and later reconciles leave it alone. No respawn loop.

Terminating and waiting before spawning keeps the replacement from holding the bot token at the same moment as the process it supersedes.

Verification

  • nix build .#checks.aarch64-darwin.unit-tests green
  • formatting and discord-transport-eval green
  • 8 new tests in test_clawde_service_sidecar_process_supersession.py, including the exactly-once replacement for a sidecar predating the record and both bounds of the termination wait

The supervisor asked pgrep for the sidecar's match pattern and, finding
anything alive, declared the sidecar reconciled. The pattern deliberately
carries no store path, so a bridge launched from an older generation of
bridge.py matches it exactly as well as the current one does. A rebuild
therefore could never put new bridge code on a running agent: monster on
chise has carried the same bridge process for two days across four system
generations.
The sidecar now records the command it was launched from beside its log,
and a live process whose record no longer matches the desired command is
terminated, waited for, and replaced. A sidecar with no record at all is
replaced once and records itself, so the first reconcile after this change
refreshes every bridge and later ones leave it alone.
Terminating and waiting before spawning keeps the replacement from holding
the bot token at the same moment as the process it supersedes, which is the
same double-answer this module already guards against for duplicates.
Agent-Machine: kira
Agent-Resume: claude --resume efee1316-8d94-438e-951e-e9c978626531
@Castrozan
Castrozan merged commit 3ae6f6b into mainAug 25, 2026
3 checks passed
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.

1 participant

@Castrozan
, '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" + '
Replace a sidecar still running superseded code by Castrozan · Pull Request #20 · Castrozan/clawde · GitHub
Skip to content

Replace a sidecar still running superseded code - #20

Merged
Castrozan merged 1 commit into
mainfrom
sidecar-stale-code-replacement
Aug 25, 2026
Merged

Replace a sidecar still running superseded code#20
Castrozan merged 1 commit into
mainfrom
sidecar-stale-code-replacement

Conversation

@Castrozan

Copy link
Copy Markdown
Owner

What was wrong

reconcile_one_sidecar_process asked pgrep for the sidecar's match pattern and, finding anything alive, returned without spawning. The pattern deliberately carries no store path, so a bridge launched from an older generation of bridge.py matches it exactly as well as the current one does.

A rebuild could therefore never put new bridge code on a running agent. Live evidence on chise: monster's bridge (pid 4193780, /nix/store/fx1wv2ds…-bridge.py) has run for two days across system generations 1618 to 1621. The media fix shipped in v0.12.5 would never have reached it.

What changed

spawn_sidecar_process records the command it launched beside the sidecar log (<log>.spawned-command). reconcile_one_sidecar_process compares that record to the desired command; on a mismatch it terminates every live process, waits for each to exit, then spawns the replacement.

The command string covers everything that can change: the bridge script's store path, the python environment, the one-shot turn command, the workspace and state directories.

A sidecar with no record at all counts as superseded, so the first reconcile after this change refreshes every bridge once and later reconciles leave it alone. No respawn loop.

Terminating and waiting before spawning keeps the replacement from holding the bot token at the same moment as the process it supersedes.

Verification

  • nix build .#checks.aarch64-darwin.unit-tests green
  • formatting and discord-transport-eval green
  • 8 new tests in test_clawde_service_sidecar_process_supersession.py, including the exactly-once replacement for a sidecar predating the record and both bounds of the termination wait

The supervisor asked pgrep for the sidecar's match pattern and, finding
anything alive, declared the sidecar reconciled. The pattern deliberately
carries no store path, so a bridge launched from an older generation of
bridge.py matches it exactly as well as the current one does. A rebuild
therefore could never put new bridge code on a running agent: monster on
chise has carried the same bridge process for two days across four system
generations.
The sidecar now records the command it was launched from beside its log,
and a live process whose record no longer matches the desired command is
terminated, waited for, and replaced. A sidecar with no record at all is
replaced once and records itself, so the first reconcile after this change
refreshes every bridge and later ones leave it alone.
Terminating and waiting before spawning keeps the replacement from holding
the bot token at the same moment as the process it supersedes, which is the
same double-answer this module already guards against for duplicates.
Agent-Machine: kira
Agent-Resume: claude --resume efee1316-8d94-438e-951e-e9c978626531
@Castrozan
Castrozan merged commit 3ae6f6b into mainAug 25, 2026
3 checks passed
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.

1 participant

@Castrozan
, '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('^' + ".*" + ' Replace a sidecar still running superseded code by Castrozan · Pull Request #20 · Castrozan/clawde · GitHub
Skip to content

Replace a sidecar still running superseded code - #20

Merged
Castrozan merged 1 commit into
mainfrom
sidecar-stale-code-replacement
Aug 25, 2026
Merged

Replace a sidecar still running superseded code#20
Castrozan merged 1 commit into
mainfrom
sidecar-stale-code-replacement

Conversation

@Castrozan

Copy link
Copy Markdown
Owner

What was wrong

reconcile_one_sidecar_process asked pgrep for the sidecar's match pattern and, finding anything alive, returned without spawning. The pattern deliberately carries no store path, so a bridge launched from an older generation of bridge.py matches it exactly as well as the current one does.

A rebuild could therefore never put new bridge code on a running agent. Live evidence on chise: monster's bridge (pid 4193780, /nix/store/fx1wv2ds…-bridge.py) has run for two days across system generations 1618 to 1621. The media fix shipped in v0.12.5 would never have reached it.

What changed

spawn_sidecar_process records the command it launched beside the sidecar log (<log>.spawned-command). reconcile_one_sidecar_process compares that record to the desired command; on a mismatch it terminates every live process, waits for each to exit, then spawns the replacement.

The command string covers everything that can change: the bridge script's store path, the python environment, the one-shot turn command, the workspace and state directories.

A sidecar with no record at all counts as superseded, so the first reconcile after this change refreshes every bridge once and later reconciles leave it alone. No respawn loop.

Terminating and waiting before spawning keeps the replacement from holding the bot token at the same moment as the process it supersedes.

Verification

  • nix build .#checks.aarch64-darwin.unit-tests green
  • formatting and discord-transport-eval green
  • 8 new tests in test_clawde_service_sidecar_process_supersession.py, including the exactly-once replacement for a sidecar predating the record and both bounds of the termination wait

The supervisor asked pgrep for the sidecar's match pattern and, finding
anything alive, declared the sidecar reconciled. The pattern deliberately
carries no store path, so a bridge launched from an older generation of
bridge.py matches it exactly as well as the current one does. A rebuild
therefore could never put new bridge code on a running agent: monster on
chise has carried the same bridge process for two days across four system
generations.
The sidecar now records the command it was launched from beside its log,
and a live process whose record no longer matches the desired command is
terminated, waited for, and replaced. A sidecar with no record at all is
replaced once and records itself, so the first reconcile after this change
refreshes every bridge and later ones leave it alone.
Terminating and waiting before spawning keeps the replacement from holding
the bot token at the same moment as the process it supersedes, which is the
same double-answer this module already guards against for duplicates.
Agent-Machine: kira
Agent-Resume: claude --resume efee1316-8d94-438e-951e-e9c978626531
@Castrozan
Castrozan merged commit 3ae6f6b into mainAug 25, 2026
3 checks passed
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.

1 participant

@Castrozan
, '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('^' + ".*" + ' Replace a sidecar still running superseded code by Castrozan · Pull Request #20 · Castrozan/clawde · GitHub
Skip to content

Replace a sidecar still running superseded code - #20

Merged
Castrozan merged 1 commit into
mainfrom
sidecar-stale-code-replacement
Aug 25, 2026
Merged

Replace a sidecar still running superseded code#20
Castrozan merged 1 commit into
mainfrom
sidecar-stale-code-replacement

Conversation

@Castrozan

Copy link
Copy Markdown
Owner

What was wrong

reconcile_one_sidecar_process asked pgrep for the sidecar's match pattern and, finding anything alive, returned without spawning. The pattern deliberately carries no store path, so a bridge launched from an older generation of bridge.py matches it exactly as well as the current one does.

A rebuild could therefore never put new bridge code on a running agent. Live evidence on chise: monster's bridge (pid 4193780, /nix/store/fx1wv2ds…-bridge.py) has run for two days across system generations 1618 to 1621. The media fix shipped in v0.12.5 would never have reached it.

What changed

spawn_sidecar_process records the command it launched beside the sidecar log (<log>.spawned-command). reconcile_one_sidecar_process compares that record to the desired command; on a mismatch it terminates every live process, waits for each to exit, then spawns the replacement.

The command string covers everything that can change: the bridge script's store path, the python environment, the one-shot turn command, the workspace and state directories.

A sidecar with no record at all counts as superseded, so the first reconcile after this change refreshes every bridge once and later reconciles leave it alone. No respawn loop.

Terminating and waiting before spawning keeps the replacement from holding the bot token at the same moment as the process it supersedes.

Verification

  • nix build .#checks.aarch64-darwin.unit-tests green
  • formatting and discord-transport-eval green
  • 8 new tests in test_clawde_service_sidecar_process_supersession.py, including the exactly-once replacement for a sidecar predating the record and both bounds of the termination wait

The supervisor asked pgrep for the sidecar's match pattern and, finding
anything alive, declared the sidecar reconciled. The pattern deliberately
carries no store path, so a bridge launched from an older generation of
bridge.py matches it exactly as well as the current one does. A rebuild
therefore could never put new bridge code on a running agent: monster on
chise has carried the same bridge process for two days across four system
generations.
The sidecar now records the command it was launched from beside its log,
and a live process whose record no longer matches the desired command is
terminated, waited for, and replaced. A sidecar with no record at all is
replaced once and records itself, so the first reconcile after this change
refreshes every bridge and later ones leave it alone.
Terminating and waiting before spawning keeps the replacement from holding
the bot token at the same moment as the process it supersedes, which is the
same double-answer this module already guards against for duplicates.
Agent-Machine: kira
Agent-Resume: claude --resume efee1316-8d94-438e-951e-e9c978626531
@Castrozan
Castrozan merged commit 3ae6f6b into mainAug 25, 2026
3 checks passed
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.

1 participant

@Castrozan
, '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" + ' Replace a sidecar still running superseded code by Castrozan · Pull Request #20 · Castrozan/clawde · GitHub
Skip to content

Replace a sidecar still running superseded code - #20

Merged
Castrozan merged 1 commit into
mainfrom
sidecar-stale-code-replacement
Aug 25, 2026
Merged

Replace a sidecar still running superseded code#20
Castrozan merged 1 commit into
mainfrom
sidecar-stale-code-replacement

Conversation

@Castrozan

Copy link
Copy Markdown
Owner

What was wrong

reconcile_one_sidecar_process asked pgrep for the sidecar's match pattern and, finding anything alive, returned without spawning. The pattern deliberately carries no store path, so a bridge launched from an older generation of bridge.py matches it exactly as well as the current one does.

A rebuild could therefore never put new bridge code on a running agent. Live evidence on chise: monster's bridge (pid 4193780, /nix/store/fx1wv2ds…-bridge.py) has run for two days across system generations 1618 to 1621. The media fix shipped in v0.12.5 would never have reached it.

What changed

spawn_sidecar_process records the command it launched beside the sidecar log (<log>.spawned-command). reconcile_one_sidecar_process compares that record to the desired command; on a mismatch it terminates every live process, waits for each to exit, then spawns the replacement.

The command string covers everything that can change: the bridge script's store path, the python environment, the one-shot turn command, the workspace and state directories.

A sidecar with no record at all counts as superseded, so the first reconcile after this change refreshes every bridge once and later reconciles leave it alone. No respawn loop.

Terminating and waiting before spawning keeps the replacement from holding the bot token at the same moment as the process it supersedes.

Verification

  • nix build .#checks.aarch64-darwin.unit-tests green
  • formatting and discord-transport-eval green
  • 8 new tests in test_clawde_service_sidecar_process_supersession.py, including the exactly-once replacement for a sidecar predating the record and both bounds of the termination wait

The supervisor asked pgrep for the sidecar's match pattern and, finding
anything alive, declared the sidecar reconciled. The pattern deliberately
carries no store path, so a bridge launched from an older generation of
bridge.py matches it exactly as well as the current one does. A rebuild
therefore could never put new bridge code on a running agent: monster on
chise has carried the same bridge process for two days across four system
generations.
The sidecar now records the command it was launched from beside its log,
and a live process whose record no longer matches the desired command is
terminated, waited for, and replaced. A sidecar with no record at all is
replaced once and records itself, so the first reconcile after this change
refreshes every bridge and later ones leave it alone.
Terminating and waiting before spawning keeps the replacement from holding
the bot token at the same moment as the process it supersedes, which is the
same double-answer this module already guards against for duplicates.
Agent-Machine: kira
Agent-Resume: claude --resume efee1316-8d94-438e-951e-e9c978626531
@Castrozan
Castrozan merged commit 3ae6f6b into mainAug 25, 2026
3 checks passed
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.

1 participant

@Castrozan
, '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('^' + ".*" + ' Replace a sidecar still running superseded code by Castrozan · Pull Request #20 · Castrozan/clawde · GitHub
Skip to content

Replace a sidecar still running superseded code - #20

Merged
Castrozan merged 1 commit into
mainfrom
sidecar-stale-code-replacement
Aug 25, 2026
Merged

Replace a sidecar still running superseded code#20
Castrozan merged 1 commit into
mainfrom
sidecar-stale-code-replacement

Conversation

@Castrozan

Copy link
Copy Markdown
Owner

What was wrong

reconcile_one_sidecar_process asked pgrep for the sidecar's match pattern and, finding anything alive, returned without spawning. The pattern deliberately carries no store path, so a bridge launched from an older generation of bridge.py matches it exactly as well as the current one does.

A rebuild could therefore never put new bridge code on a running agent. Live evidence on chise: monster's bridge (pid 4193780, /nix/store/fx1wv2ds…-bridge.py) has run for two days across system generations 1618 to 1621. The media fix shipped in v0.12.5 would never have reached it.

What changed

spawn_sidecar_process records the command it launched beside the sidecar log (<log>.spawned-command). reconcile_one_sidecar_process compares that record to the desired command; on a mismatch it terminates every live process, waits for each to exit, then spawns the replacement.

The command string covers everything that can change: the bridge script's store path, the python environment, the one-shot turn command, the workspace and state directories.

A sidecar with no record at all counts as superseded, so the first reconcile after this change refreshes every bridge once and later reconciles leave it alone. No respawn loop.

Terminating and waiting before spawning keeps the replacement from holding the bot token at the same moment as the process it supersedes.

Verification

  • nix build .#checks.aarch64-darwin.unit-tests green
  • formatting and discord-transport-eval green
  • 8 new tests in test_clawde_service_sidecar_process_supersession.py, including the exactly-once replacement for a sidecar predating the record and both bounds of the termination wait

The supervisor asked pgrep for the sidecar's match pattern and, finding
anything alive, declared the sidecar reconciled. The pattern deliberately
carries no store path, so a bridge launched from an older generation of
bridge.py matches it exactly as well as the current one does. A rebuild
therefore could never put new bridge code on a running agent: monster on
chise has carried the same bridge process for two days across four system
generations.
The sidecar now records the command it was launched from beside its log,
and a live process whose record no longer matches the desired command is
terminated, waited for, and replaced. A sidecar with no record at all is
replaced once and records itself, so the first reconcile after this change
refreshes every bridge and later ones leave it alone.
Terminating and waiting before spawning keeps the replacement from holding
the bot token at the same moment as the process it supersedes, which is the
same double-answer this module already guards against for duplicates.
Agent-Machine: kira
Agent-Resume: claude --resume efee1316-8d94-438e-951e-e9c978626531
@Castrozan
Castrozan merged commit 3ae6f6b into mainAug 25, 2026
3 checks passed
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.

1 participant

@Castrozan
, '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); } })(); })(); Replace a sidecar still running superseded code by Castrozan · Pull Request #20 · Castrozan/clawde · GitHub
Skip to content

Replace a sidecar still running superseded code - #20

Merged
Castrozan merged 1 commit into
mainfrom
sidecar-stale-code-replacement
Aug 25, 2026
Merged

Replace a sidecar still running superseded code#20
Castrozan merged 1 commit into
mainfrom
sidecar-stale-code-replacement

Conversation

@Castrozan

Copy link
Copy Markdown
Owner

What was wrong

reconcile_one_sidecar_process asked pgrep for the sidecar's match pattern and, finding anything alive, returned without spawning. The pattern deliberately carries no store path, so a bridge launched from an older generation of bridge.py matches it exactly as well as the current one does.

A rebuild could therefore never put new bridge code on a running agent. Live evidence on chise: monster's bridge (pid 4193780, /nix/store/fx1wv2ds…-bridge.py) has run for two days across system generations 1618 to 1621. The media fix shipped in v0.12.5 would never have reached it.

What changed

spawn_sidecar_process records the command it launched beside the sidecar log (<log>.spawned-command). reconcile_one_sidecar_process compares that record to the desired command; on a mismatch it terminates every live process, waits for each to exit, then spawns the replacement.

The command string covers everything that can change: the bridge script's store path, the python environment, the one-shot turn command, the workspace and state directories.

A sidecar with no record at all counts as superseded, so the first reconcile after this change refreshes every bridge once and later reconciles leave it alone. No respawn loop.

Terminating and waiting before spawning keeps the replacement from holding the bot token at the same moment as the process it supersedes.

Verification

  • nix build .#checks.aarch64-darwin.unit-tests green
  • formatting and discord-transport-eval green
  • 8 new tests in test_clawde_service_sidecar_process_supersession.py, including the exactly-once replacement for a sidecar predating the record and both bounds of the termination wait

The supervisor asked pgrep for the sidecar's match pattern and, finding
anything alive, declared the sidecar reconciled. The pattern deliberately
carries no store path, so a bridge launched from an older generation of
bridge.py matches it exactly as well as the current one does. A rebuild
therefore could never put new bridge code on a running agent: monster on
chise has carried the same bridge process for two days across four system
generations.
The sidecar now records the command it was launched from beside its log,
and a live process whose record no longer matches the desired command is
terminated, waited for, and replaced. A sidecar with no record at all is
replaced once and records itself, so the first reconcile after this change
refreshes every bridge and later ones leave it alone.
Terminating and waiting before spawning keeps the replacement from holding
the bot token at the same moment as the process it supersedes, which is the
same double-answer this module already guards against for duplicates.
Agent-Machine: kira
Agent-Resume: claude --resume efee1316-8d94-438e-951e-e9c978626531
@Castrozan
Castrozan merged commit 3ae6f6b into mainAug 25, 2026
3 checks passed
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.

1 participant

@Castrozan