fix(server): bound orchestration replay payloads - #8992

Merged
t3dotgg merged 2 commits into
pingdotgg:mainfrom
rcawston:fix/bound-orchestration-replay-payloads
Sep 2, 2026
Merged

fix(server): bound orchestration replay payloads#8992
t3dotgg merged 2 commits into
pingdotgg:mainfrom
rcawston:fix/bound-orchestration-replay-payloads

Conversation

@rcawston

@rcawstonrcawston commented Sep 1, 2026

Copy link
Copy Markdown

What Changed

  • Add a lightweight replay-range query that counts events and serialized payload bytes without decoding them.
  • Fall back to a fresh snapshot when a reconnect gap exceeds the existing row limit or an 8 MiB payload budget.
  • Apply the same preflight behavior to thread and shell subscriptions.
  • Add focused coverage for oversized replay gaps and snapshot fallback behavior.

Why

Severity: high (regular-use blocker).

WebSocket catch-up checked only the number of missing events, then loaded and decoded the entire replay range before deciding whether it was safe to send. A small number of events can contain very large tool payloads, so reconnecting a client to a busy conversation could allocate multiple gigabytes, exhaust V8, restart the backend, and leave the client repeatedly reconnecting without a usable conversation.

Measuring the serialized range first lets the server choose the existing snapshot path before materializing unsafe payloads.

Checklist

  • This PR is small and focused
  • I explained what changed and why
  • I included before/after screenshots for any UI changes
  • I included a video for animation/interaction changes

Note

Bound WebSocket orchestration replay payloads to 8 MiB and add getEventReplayStats

  • Adds getEventReplayStats to the ProjectionSnapshotQuery service contract and SQL implementation, returning event count and serialized payload bytes for an exclusive-lower/inclusive-upper sequence range without decoding payloads.
  • Adds canReplayPersistedRange in ws.ts that rejects ranges ahead of the authoritative head, exceeding the event-gap limit, or exceeding the new 8 MiB serialized-payload budget.
  • Shell and thread subscription resume paths now call this helper before reading/decoding events; rejected ranges emit a fresh snapshot followed by synchronized/live updates.
  • Risk: subscriptions whose persisted ranges now exceed the 8 MiB budget or head/limit checks will silently switch from replay to snapshot delivery instead of replaying events — reviewers should check canReplayPersistedRange in ws.ts and the getEventReplayStats query in ProjectionSnapshotQuery.ts for boundary correctness.

Macroscope summarized adb6780.


Note

Medium Risk
Changes WebSocket reconnect/catch-up behavior for shell and thread subscriptions; incorrect byte accounting could still allow heavy replay or over-snapshot clients, but the path is defensive and well-tested.

Overview
WebSocket subscribeShell and subscribeThread catch-up used to allow replay whenever the sequence gap was within the existing ~1,000-event cap, which could still decode huge tool payloads and OOM the server.

This PR adds getEventReplayStats on ProjectionSnapshotQuery, aggregating event count and serialized payload_json size in SQL via octet_length (no payload decode). canReplayPersistedRange in ws.ts requires both the gap limit and an 8 MiB payload budget (ORCHESTRATION_REPLAY_PAYLOAD_BUDGET_BYTES); otherwise the client gets snapshot + synchronized instead of replay. Test mocks are updated, plus coverage for byte measurement (including emoji) and subscription fallback when stats exceed the budget.

Reviewed by Cursor Bugbot for commit adb6780. Bugbot is set up for automated code reviews on this repo. Configure here.

@github-actionsgithub-actionsBot added vouch:unvouched PR author is not yet trusted in the VOUCHED list. size:L 100-499 changed lines (additions + deletions). labels Sep 1, 2026
Comment threadapps/server/src/ws.ts
Comment threadapps/server/src/orchestration/Layers/ProjectionSnapshotQuery.ts Outdated
@macroscopeapp

macroscopeappBot commented Sep 1, 2026

Copy link
Copy Markdown
Contributor

Approvability

Verdict: Not approved

Macroscope's review found this PR not approvable — This changes production WebSocket reconnect behavior by adding an always-on 8 MiB policy that can replace event replay with snapshot delivery and introduces a new persistence query used by both shell and thread subscriptions. The focused tests reduce risk, but the autonomous gate and its customer-visible fallback behavior warrant human review.

You can add or adjust custom eligibility rules. Learn more.

rcawstonand others added 2 commits September 1, 2026 17:40
SQLite length() on TEXT counts code points, so non-ASCII payloads could
pass the 8 MiB budget at up to four times their byte size. Use
octet_length() and cover a multibyte row in the stats test.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@t3dotgg
t3dotggforce-pushed the fix/bound-orchestration-replay-payloads branch from c693c76 to adb6780CompareSeptember 2, 2026 00:46
@t3dotgg
t3dotgg merged commit 7e460f4 into pingdotgg:mainSep 2, 2026
20 checks passed
juliusmarminge added a commit that referenced this pull request Sep 2, 2026
Adopts main's round-19 features into the v2 stack: payload-budgeted
orchestration replay (#8992), sidebar row subscription leases (#9052),
tool group virtualization and scroll anchoring (#9106), repeated-command
and browser-group presentation, inline assistant citations (#9146),
per-cwd provider skills discovery (#8778), Claude composer skill
dispatch (#9128), grok health probe and model negotiation (#9154), and
the failed-tool thinking fallback (#9165).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
juliusmarminge added a commit that referenced this pull request Sep 2, 2026
Adopts main's round-19 features into the v2 stack: payload-budgeted
orchestration replay (#8992), sidebar row subscription leases (#9052),
tool group virtualization and scroll anchoring (#9106), repeated-command
and browser-group presentation, inline assistant citations (#9146),
per-cwd provider skills discovery (#8778), Claude composer skill
dispatch (#9128), grok health probe and model negotiation (#9154), and
the failed-tool thinking fallback (#9165).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:L100-499 changed lines (additions + deletions).vouch:unvouchedPR author is not yet trusted in the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@rcawston@t3dotgg
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n 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;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

fix(server): bound orchestration replay payloads - #8992

Merged
t3dotgg merged 2 commits into
pingdotgg:mainfrom
rcawston:fix/bound-orchestration-replay-payloads
Sep 2, 2026
Merged

fix(server): bound orchestration replay payloads#8992
t3dotgg merged 2 commits into
pingdotgg:mainfrom
rcawston:fix/bound-orchestration-replay-payloads

Conversation

@rcawston

@rcawstonrcawston commented Sep 1, 2026

Copy link
Copy Markdown

What Changed

  • Add a lightweight replay-range query that counts events and serialized payload bytes without decoding them.
  • Fall back to a fresh snapshot when a reconnect gap exceeds the existing row limit or an 8 MiB payload budget.
  • Apply the same preflight behavior to thread and shell subscriptions.
  • Add focused coverage for oversized replay gaps and snapshot fallback behavior.

Why

Severity: high (regular-use blocker).

WebSocket catch-up checked only the number of missing events, then loaded and decoded the entire replay range before deciding whether it was safe to send. A small number of events can contain very large tool payloads, so reconnecting a client to a busy conversation could allocate multiple gigabytes, exhaust V8, restart the backend, and leave the client repeatedly reconnecting without a usable conversation.

Measuring the serialized range first lets the server choose the existing snapshot path before materializing unsafe payloads.

Checklist

  • This PR is small and focused
  • I explained what changed and why
  • I included before/after screenshots for any UI changes
  • I included a video for animation/interaction changes

Note

Bound WebSocket orchestration replay payloads to 8 MiB and add getEventReplayStats

  • Adds getEventReplayStats to the ProjectionSnapshotQuery service contract and SQL implementation, returning event count and serialized payload bytes for an exclusive-lower/inclusive-upper sequence range without decoding payloads.
  • Adds canReplayPersistedRange in ws.ts that rejects ranges ahead of the authoritative head, exceeding the event-gap limit, or exceeding the new 8 MiB serialized-payload budget.
  • Shell and thread subscription resume paths now call this helper before reading/decoding events; rejected ranges emit a fresh snapshot followed by synchronized/live updates.
  • Risk: subscriptions whose persisted ranges now exceed the 8 MiB budget or head/limit checks will silently switch from replay to snapshot delivery instead of replaying events — reviewers should check canReplayPersistedRange in ws.ts and the getEventReplayStats query in ProjectionSnapshotQuery.ts for boundary correctness.

Macroscope summarized adb6780.


Note

Medium Risk
Changes WebSocket reconnect/catch-up behavior for shell and thread subscriptions; incorrect byte accounting could still allow heavy replay or over-snapshot clients, but the path is defensive and well-tested.

Overview
WebSocket subscribeShell and subscribeThread catch-up used to allow replay whenever the sequence gap was within the existing ~1,000-event cap, which could still decode huge tool payloads and OOM the server.

This PR adds getEventReplayStats on ProjectionSnapshotQuery, aggregating event count and serialized payload_json size in SQL via octet_length (no payload decode). canReplayPersistedRange in ws.ts requires both the gap limit and an 8 MiB payload budget (ORCHESTRATION_REPLAY_PAYLOAD_BUDGET_BYTES); otherwise the client gets snapshot + synchronized instead of replay. Test mocks are updated, plus coverage for byte measurement (including emoji) and subscription fallback when stats exceed the budget.

Reviewed by Cursor Bugbot for commit adb6780. Bugbot is set up for automated code reviews on this repo. Configure here.

@github-actionsgithub-actionsBot added vouch:unvouched PR author is not yet trusted in the VOUCHED list. size:L 100-499 changed lines (additions + deletions). labels Sep 1, 2026
Comment threadapps/server/src/ws.ts
Comment threadapps/server/src/orchestration/Layers/ProjectionSnapshotQuery.ts Outdated
@macroscopeapp

macroscopeappBot commented Sep 1, 2026

Copy link
Copy Markdown
Contributor

Approvability

Verdict: Not approved

Macroscope's review found this PR not approvable — This changes production WebSocket reconnect behavior by adding an always-on 8 MiB policy that can replace event replay with snapshot delivery and introduces a new persistence query used by both shell and thread subscriptions. The focused tests reduce risk, but the autonomous gate and its customer-visible fallback behavior warrant human review.

You can add or adjust custom eligibility rules. Learn more.

rcawstonand others added 2 commits September 1, 2026 17:40
SQLite length() on TEXT counts code points, so non-ASCII payloads could
pass the 8 MiB budget at up to four times their byte size. Use
octet_length() and cover a multibyte row in the stats test.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@t3dotgg
t3dotggforce-pushed the fix/bound-orchestration-replay-payloads branch from c693c76 to adb6780CompareSeptember 2, 2026 00:46
@t3dotgg
t3dotgg merged commit 7e460f4 into pingdotgg:mainSep 2, 2026
20 checks passed
juliusmarminge added a commit that referenced this pull request Sep 2, 2026
Adopts main's round-19 features into the v2 stack: payload-budgeted
orchestration replay (#8992), sidebar row subscription leases (#9052),
tool group virtualization and scroll anchoring (#9106), repeated-command
and browser-group presentation, inline assistant citations (#9146),
per-cwd provider skills discovery (#8778), Claude composer skill
dispatch (#9128), grok health probe and model negotiation (#9154), and
the failed-tool thinking fallback (#9165).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
juliusmarminge added a commit that referenced this pull request Sep 2, 2026
Adopts main's round-19 features into the v2 stack: payload-budgeted
orchestration replay (#8992), sidebar row subscription leases (#9052),
tool group virtualization and scroll anchoring (#9106), repeated-command
and browser-group presentation, inline assistant citations (#9146),
per-cwd provider skills discovery (#8778), Claude composer skill
dispatch (#9128), grok health probe and model negotiation (#9154), and
the failed-tool thinking fallback (#9165).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:L100-499 changed lines (additions + deletions).vouch:unvouchedPR author is not yet trusted in the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@rcawston@t3dotgg
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

fix(server): bound orchestration replay payloads - #8992

Merged
t3dotgg merged 2 commits into
pingdotgg:mainfrom
rcawston:fix/bound-orchestration-replay-payloads
Sep 2, 2026
Merged

fix(server): bound orchestration replay payloads#8992
t3dotgg merged 2 commits into
pingdotgg:mainfrom
rcawston:fix/bound-orchestration-replay-payloads

Conversation

@rcawston

@rcawstonrcawston commented Sep 1, 2026

Copy link
Copy Markdown

What Changed

  • Add a lightweight replay-range query that counts events and serialized payload bytes without decoding them.
  • Fall back to a fresh snapshot when a reconnect gap exceeds the existing row limit or an 8 MiB payload budget.
  • Apply the same preflight behavior to thread and shell subscriptions.
  • Add focused coverage for oversized replay gaps and snapshot fallback behavior.

Why

Severity: high (regular-use blocker).

WebSocket catch-up checked only the number of missing events, then loaded and decoded the entire replay range before deciding whether it was safe to send. A small number of events can contain very large tool payloads, so reconnecting a client to a busy conversation could allocate multiple gigabytes, exhaust V8, restart the backend, and leave the client repeatedly reconnecting without a usable conversation.

Measuring the serialized range first lets the server choose the existing snapshot path before materializing unsafe payloads.

Checklist

  • This PR is small and focused
  • I explained what changed and why
  • I included before/after screenshots for any UI changes
  • I included a video for animation/interaction changes

Note

Bound WebSocket orchestration replay payloads to 8 MiB and add getEventReplayStats

  • Adds getEventReplayStats to the ProjectionSnapshotQuery service contract and SQL implementation, returning event count and serialized payload bytes for an exclusive-lower/inclusive-upper sequence range without decoding payloads.
  • Adds canReplayPersistedRange in ws.ts that rejects ranges ahead of the authoritative head, exceeding the event-gap limit, or exceeding the new 8 MiB serialized-payload budget.
  • Shell and thread subscription resume paths now call this helper before reading/decoding events; rejected ranges emit a fresh snapshot followed by synchronized/live updates.
  • Risk: subscriptions whose persisted ranges now exceed the 8 MiB budget or head/limit checks will silently switch from replay to snapshot delivery instead of replaying events — reviewers should check canReplayPersistedRange in ws.ts and the getEventReplayStats query in ProjectionSnapshotQuery.ts for boundary correctness.

Macroscope summarized adb6780.


Note

Medium Risk
Changes WebSocket reconnect/catch-up behavior for shell and thread subscriptions; incorrect byte accounting could still allow heavy replay or over-snapshot clients, but the path is defensive and well-tested.

Overview
WebSocket subscribeShell and subscribeThread catch-up used to allow replay whenever the sequence gap was within the existing ~1,000-event cap, which could still decode huge tool payloads and OOM the server.

This PR adds getEventReplayStats on ProjectionSnapshotQuery, aggregating event count and serialized payload_json size in SQL via octet_length (no payload decode). canReplayPersistedRange in ws.ts requires both the gap limit and an 8 MiB payload budget (ORCHESTRATION_REPLAY_PAYLOAD_BUDGET_BYTES); otherwise the client gets snapshot + synchronized instead of replay. Test mocks are updated, plus coverage for byte measurement (including emoji) and subscription fallback when stats exceed the budget.

Reviewed by Cursor Bugbot for commit adb6780. Bugbot is set up for automated code reviews on this repo. Configure here.

@github-actionsgithub-actionsBot added vouch:unvouched PR author is not yet trusted in the VOUCHED list. size:L 100-499 changed lines (additions + deletions). labels Sep 1, 2026
Comment threadapps/server/src/ws.ts
Comment threadapps/server/src/orchestration/Layers/ProjectionSnapshotQuery.ts Outdated
@macroscopeapp

macroscopeappBot commented Sep 1, 2026

Copy link
Copy Markdown
Contributor

Approvability

Verdict: Not approved

Macroscope's review found this PR not approvable — This changes production WebSocket reconnect behavior by adding an always-on 8 MiB policy that can replace event replay with snapshot delivery and introduces a new persistence query used by both shell and thread subscriptions. The focused tests reduce risk, but the autonomous gate and its customer-visible fallback behavior warrant human review.

You can add or adjust custom eligibility rules. Learn more.

rcawstonand others added 2 commits September 1, 2026 17:40
SQLite length() on TEXT counts code points, so non-ASCII payloads could
pass the 8 MiB budget at up to four times their byte size. Use
octet_length() and cover a multibyte row in the stats test.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@t3dotgg
t3dotggforce-pushed the fix/bound-orchestration-replay-payloads branch from c693c76 to adb6780CompareSeptember 2, 2026 00:46
@t3dotgg
t3dotgg merged commit 7e460f4 into pingdotgg:mainSep 2, 2026
20 checks passed
juliusmarminge added a commit that referenced this pull request Sep 2, 2026
Adopts main's round-19 features into the v2 stack: payload-budgeted
orchestration replay (#8992), sidebar row subscription leases (#9052),
tool group virtualization and scroll anchoring (#9106), repeated-command
and browser-group presentation, inline assistant citations (#9146),
per-cwd provider skills discovery (#8778), Claude composer skill
dispatch (#9128), grok health probe and model negotiation (#9154), and
the failed-tool thinking fallback (#9165).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
juliusmarminge added a commit that referenced this pull request Sep 2, 2026
Adopts main's round-19 features into the v2 stack: payload-budgeted
orchestration replay (#8992), sidebar row subscription leases (#9052),
tool group virtualization and scroll anchoring (#9106), repeated-command
and browser-group presentation, inline assistant citations (#9146),
per-cwd provider skills discovery (#8778), Claude composer skill
dispatch (#9128), grok health probe and model negotiation (#9154), and
the failed-tool thinking fallback (#9165).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:L100-499 changed lines (additions + deletions).vouch:unvouchedPR author is not yet trusted in the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

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

fix(server): bound orchestration replay payloads - #8992

Merged
t3dotgg merged 2 commits into
pingdotgg:mainfrom
rcawston:fix/bound-orchestration-replay-payloads
Sep 2, 2026
Merged

fix(server): bound orchestration replay payloads#8992
t3dotgg merged 2 commits into
pingdotgg:mainfrom
rcawston:fix/bound-orchestration-replay-payloads

Conversation

@rcawston

@rcawstonrcawston commented Sep 1, 2026

Copy link
Copy Markdown

What Changed

  • Add a lightweight replay-range query that counts events and serialized payload bytes without decoding them.
  • Fall back to a fresh snapshot when a reconnect gap exceeds the existing row limit or an 8 MiB payload budget.
  • Apply the same preflight behavior to thread and shell subscriptions.
  • Add focused coverage for oversized replay gaps and snapshot fallback behavior.

Why

Severity: high (regular-use blocker).

WebSocket catch-up checked only the number of missing events, then loaded and decoded the entire replay range before deciding whether it was safe to send. A small number of events can contain very large tool payloads, so reconnecting a client to a busy conversation could allocate multiple gigabytes, exhaust V8, restart the backend, and leave the client repeatedly reconnecting without a usable conversation.

Measuring the serialized range first lets the server choose the existing snapshot path before materializing unsafe payloads.

Checklist

  • This PR is small and focused
  • I explained what changed and why
  • I included before/after screenshots for any UI changes
  • I included a video for animation/interaction changes

Note

Bound WebSocket orchestration replay payloads to 8 MiB and add getEventReplayStats

  • Adds getEventReplayStats to the ProjectionSnapshotQuery service contract and SQL implementation, returning event count and serialized payload bytes for an exclusive-lower/inclusive-upper sequence range without decoding payloads.
  • Adds canReplayPersistedRange in ws.ts that rejects ranges ahead of the authoritative head, exceeding the event-gap limit, or exceeding the new 8 MiB serialized-payload budget.
  • Shell and thread subscription resume paths now call this helper before reading/decoding events; rejected ranges emit a fresh snapshot followed by synchronized/live updates.
  • Risk: subscriptions whose persisted ranges now exceed the 8 MiB budget or head/limit checks will silently switch from replay to snapshot delivery instead of replaying events — reviewers should check canReplayPersistedRange in ws.ts and the getEventReplayStats query in ProjectionSnapshotQuery.ts for boundary correctness.

Macroscope summarized adb6780.


Note

Medium Risk
Changes WebSocket reconnect/catch-up behavior for shell and thread subscriptions; incorrect byte accounting could still allow heavy replay or over-snapshot clients, but the path is defensive and well-tested.

Overview
WebSocket subscribeShell and subscribeThread catch-up used to allow replay whenever the sequence gap was within the existing ~1,000-event cap, which could still decode huge tool payloads and OOM the server.

This PR adds getEventReplayStats on ProjectionSnapshotQuery, aggregating event count and serialized payload_json size in SQL via octet_length (no payload decode). canReplayPersistedRange in ws.ts requires both the gap limit and an 8 MiB payload budget (ORCHESTRATION_REPLAY_PAYLOAD_BUDGET_BYTES); otherwise the client gets snapshot + synchronized instead of replay. Test mocks are updated, plus coverage for byte measurement (including emoji) and subscription fallback when stats exceed the budget.

Reviewed by Cursor Bugbot for commit adb6780. Bugbot is set up for automated code reviews on this repo. Configure here.

@github-actionsgithub-actionsBot added vouch:unvouched PR author is not yet trusted in the VOUCHED list. size:L 100-499 changed lines (additions + deletions). labels Sep 1, 2026
Comment threadapps/server/src/ws.ts
Comment threadapps/server/src/orchestration/Layers/ProjectionSnapshotQuery.ts Outdated
@macroscopeapp

macroscopeappBot commented Sep 1, 2026

Copy link
Copy Markdown
Contributor

Approvability

Verdict: Not approved

Macroscope's review found this PR not approvable — This changes production WebSocket reconnect behavior by adding an always-on 8 MiB policy that can replace event replay with snapshot delivery and introduces a new persistence query used by both shell and thread subscriptions. The focused tests reduce risk, but the autonomous gate and its customer-visible fallback behavior warrant human review.

You can add or adjust custom eligibility rules. Learn more.

rcawstonand others added 2 commits September 1, 2026 17:40
SQLite length() on TEXT counts code points, so non-ASCII payloads could
pass the 8 MiB budget at up to four times their byte size. Use
octet_length() and cover a multibyte row in the stats test.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@t3dotgg
t3dotggforce-pushed the fix/bound-orchestration-replay-payloads branch from c693c76 to adb6780CompareSeptember 2, 2026 00:46
@t3dotgg
t3dotgg merged commit 7e460f4 into pingdotgg:mainSep 2, 2026
20 checks passed
juliusmarminge added a commit that referenced this pull request Sep 2, 2026
Adopts main's round-19 features into the v2 stack: payload-budgeted
orchestration replay (#8992), sidebar row subscription leases (#9052),
tool group virtualization and scroll anchoring (#9106), repeated-command
and browser-group presentation, inline assistant citations (#9146),
per-cwd provider skills discovery (#8778), Claude composer skill
dispatch (#9128), grok health probe and model negotiation (#9154), and
the failed-tool thinking fallback (#9165).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
juliusmarminge added a commit that referenced this pull request Sep 2, 2026
Adopts main's round-19 features into the v2 stack: payload-budgeted
orchestration replay (#8992), sidebar row subscription leases (#9052),
tool group virtualization and scroll anchoring (#9106), repeated-command
and browser-group presentation, inline assistant citations (#9146),
per-cwd provider skills discovery (#8778), Claude composer skill
dispatch (#9128), grok health probe and model negotiation (#9154), and
the failed-tool thinking fallback (#9165).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:L100-499 changed lines (additions + deletions).vouch:unvouchedPR author is not yet trusted in the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@rcawston@t3dotgg
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

fix(server): bound orchestration replay payloads - #8992

Merged
t3dotgg merged 2 commits into
pingdotgg:mainfrom
rcawston:fix/bound-orchestration-replay-payloads
Sep 2, 2026
Merged

fix(server): bound orchestration replay payloads#8992
t3dotgg merged 2 commits into
pingdotgg:mainfrom
rcawston:fix/bound-orchestration-replay-payloads

Conversation

@rcawston

@rcawstonrcawston commented Sep 1, 2026

Copy link
Copy Markdown

What Changed

  • Add a lightweight replay-range query that counts events and serialized payload bytes without decoding them.
  • Fall back to a fresh snapshot when a reconnect gap exceeds the existing row limit or an 8 MiB payload budget.
  • Apply the same preflight behavior to thread and shell subscriptions.
  • Add focused coverage for oversized replay gaps and snapshot fallback behavior.

Why

Severity: high (regular-use blocker).

WebSocket catch-up checked only the number of missing events, then loaded and decoded the entire replay range before deciding whether it was safe to send. A small number of events can contain very large tool payloads, so reconnecting a client to a busy conversation could allocate multiple gigabytes, exhaust V8, restart the backend, and leave the client repeatedly reconnecting without a usable conversation.

Measuring the serialized range first lets the server choose the existing snapshot path before materializing unsafe payloads.

Checklist

  • This PR is small and focused
  • I explained what changed and why
  • I included before/after screenshots for any UI changes
  • I included a video for animation/interaction changes

Note

Bound WebSocket orchestration replay payloads to 8 MiB and add getEventReplayStats

  • Adds getEventReplayStats to the ProjectionSnapshotQuery service contract and SQL implementation, returning event count and serialized payload bytes for an exclusive-lower/inclusive-upper sequence range without decoding payloads.
  • Adds canReplayPersistedRange in ws.ts that rejects ranges ahead of the authoritative head, exceeding the event-gap limit, or exceeding the new 8 MiB serialized-payload budget.
  • Shell and thread subscription resume paths now call this helper before reading/decoding events; rejected ranges emit a fresh snapshot followed by synchronized/live updates.
  • Risk: subscriptions whose persisted ranges now exceed the 8 MiB budget or head/limit checks will silently switch from replay to snapshot delivery instead of replaying events — reviewers should check canReplayPersistedRange in ws.ts and the getEventReplayStats query in ProjectionSnapshotQuery.ts for boundary correctness.

Macroscope summarized adb6780.


Note

Medium Risk
Changes WebSocket reconnect/catch-up behavior for shell and thread subscriptions; incorrect byte accounting could still allow heavy replay or over-snapshot clients, but the path is defensive and well-tested.

Overview
WebSocket subscribeShell and subscribeThread catch-up used to allow replay whenever the sequence gap was within the existing ~1,000-event cap, which could still decode huge tool payloads and OOM the server.

This PR adds getEventReplayStats on ProjectionSnapshotQuery, aggregating event count and serialized payload_json size in SQL via octet_length (no payload decode). canReplayPersistedRange in ws.ts requires both the gap limit and an 8 MiB payload budget (ORCHESTRATION_REPLAY_PAYLOAD_BUDGET_BYTES); otherwise the client gets snapshot + synchronized instead of replay. Test mocks are updated, plus coverage for byte measurement (including emoji) and subscription fallback when stats exceed the budget.

Reviewed by Cursor Bugbot for commit adb6780. Bugbot is set up for automated code reviews on this repo. Configure here.

@github-actionsgithub-actionsBot added vouch:unvouched PR author is not yet trusted in the VOUCHED list. size:L 100-499 changed lines (additions + deletions). labels Sep 1, 2026
Comment threadapps/server/src/ws.ts
Comment threadapps/server/src/orchestration/Layers/ProjectionSnapshotQuery.ts Outdated
@macroscopeapp

macroscopeappBot commented Sep 1, 2026

Copy link
Copy Markdown
Contributor

Approvability

Verdict: Not approved

Macroscope's review found this PR not approvable — This changes production WebSocket reconnect behavior by adding an always-on 8 MiB policy that can replace event replay with snapshot delivery and introduces a new persistence query used by both shell and thread subscriptions. The focused tests reduce risk, but the autonomous gate and its customer-visible fallback behavior warrant human review.

You can add or adjust custom eligibility rules. Learn more.

rcawstonand others added 2 commits September 1, 2026 17:40
SQLite length() on TEXT counts code points, so non-ASCII payloads could
pass the 8 MiB budget at up to four times their byte size. Use
octet_length() and cover a multibyte row in the stats test.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@t3dotgg
t3dotggforce-pushed the fix/bound-orchestration-replay-payloads branch from c693c76 to adb6780CompareSeptember 2, 2026 00:46
@t3dotgg
t3dotgg merged commit 7e460f4 into pingdotgg:mainSep 2, 2026
20 checks passed
juliusmarminge added a commit that referenced this pull request Sep 2, 2026
Adopts main's round-19 features into the v2 stack: payload-budgeted
orchestration replay (#8992), sidebar row subscription leases (#9052),
tool group virtualization and scroll anchoring (#9106), repeated-command
and browser-group presentation, inline assistant citations (#9146),
per-cwd provider skills discovery (#8778), Claude composer skill
dispatch (#9128), grok health probe and model negotiation (#9154), and
the failed-tool thinking fallback (#9165).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
juliusmarminge added a commit that referenced this pull request Sep 2, 2026
Adopts main's round-19 features into the v2 stack: payload-budgeted
orchestration replay (#8992), sidebar row subscription leases (#9052),
tool group virtualization and scroll anchoring (#9106), repeated-command
and browser-group presentation, inline assistant citations (#9146),
per-cwd provider skills discovery (#8778), Claude composer skill
dispatch (#9128), grok health probe and model negotiation (#9154), and
the failed-tool thinking fallback (#9165).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:L100-499 changed lines (additions + deletions).vouch:unvouchedPR author is not yet trusted in the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@rcawston@t3dotgg
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

fix(server): bound orchestration replay payloads - #8992

Merged
t3dotgg merged 2 commits into
pingdotgg:mainfrom
rcawston:fix/bound-orchestration-replay-payloads
Sep 2, 2026
Merged

fix(server): bound orchestration replay payloads#8992
t3dotgg merged 2 commits into
pingdotgg:mainfrom
rcawston:fix/bound-orchestration-replay-payloads

Conversation

@rcawston

@rcawstonrcawston commented Sep 1, 2026

Copy link
Copy Markdown

What Changed

  • Add a lightweight replay-range query that counts events and serialized payload bytes without decoding them.
  • Fall back to a fresh snapshot when a reconnect gap exceeds the existing row limit or an 8 MiB payload budget.
  • Apply the same preflight behavior to thread and shell subscriptions.
  • Add focused coverage for oversized replay gaps and snapshot fallback behavior.

Why

Severity: high (regular-use blocker).

WebSocket catch-up checked only the number of missing events, then loaded and decoded the entire replay range before deciding whether it was safe to send. A small number of events can contain very large tool payloads, so reconnecting a client to a busy conversation could allocate multiple gigabytes, exhaust V8, restart the backend, and leave the client repeatedly reconnecting without a usable conversation.

Measuring the serialized range first lets the server choose the existing snapshot path before materializing unsafe payloads.

Checklist

  • This PR is small and focused
  • I explained what changed and why
  • I included before/after screenshots for any UI changes
  • I included a video for animation/interaction changes

Note

Bound WebSocket orchestration replay payloads to 8 MiB and add getEventReplayStats

  • Adds getEventReplayStats to the ProjectionSnapshotQuery service contract and SQL implementation, returning event count and serialized payload bytes for an exclusive-lower/inclusive-upper sequence range without decoding payloads.
  • Adds canReplayPersistedRange in ws.ts that rejects ranges ahead of the authoritative head, exceeding the event-gap limit, or exceeding the new 8 MiB serialized-payload budget.
  • Shell and thread subscription resume paths now call this helper before reading/decoding events; rejected ranges emit a fresh snapshot followed by synchronized/live updates.
  • Risk: subscriptions whose persisted ranges now exceed the 8 MiB budget or head/limit checks will silently switch from replay to snapshot delivery instead of replaying events — reviewers should check canReplayPersistedRange in ws.ts and the getEventReplayStats query in ProjectionSnapshotQuery.ts for boundary correctness.

Macroscope summarized adb6780.


Note

Medium Risk
Changes WebSocket reconnect/catch-up behavior for shell and thread subscriptions; incorrect byte accounting could still allow heavy replay or over-snapshot clients, but the path is defensive and well-tested.

Overview
WebSocket subscribeShell and subscribeThread catch-up used to allow replay whenever the sequence gap was within the existing ~1,000-event cap, which could still decode huge tool payloads and OOM the server.

This PR adds getEventReplayStats on ProjectionSnapshotQuery, aggregating event count and serialized payload_json size in SQL via octet_length (no payload decode). canReplayPersistedRange in ws.ts requires both the gap limit and an 8 MiB payload budget (ORCHESTRATION_REPLAY_PAYLOAD_BUDGET_BYTES); otherwise the client gets snapshot + synchronized instead of replay. Test mocks are updated, plus coverage for byte measurement (including emoji) and subscription fallback when stats exceed the budget.

Reviewed by Cursor Bugbot for commit adb6780. Bugbot is set up for automated code reviews on this repo. Configure here.

@github-actionsgithub-actionsBot added vouch:unvouched PR author is not yet trusted in the VOUCHED list. size:L 100-499 changed lines (additions + deletions). labels Sep 1, 2026
Comment threadapps/server/src/ws.ts
Comment threadapps/server/src/orchestration/Layers/ProjectionSnapshotQuery.ts Outdated
@macroscopeapp

macroscopeappBot commented Sep 1, 2026

Copy link
Copy Markdown
Contributor

Approvability

Verdict: Not approved

Macroscope's review found this PR not approvable — This changes production WebSocket reconnect behavior by adding an always-on 8 MiB policy that can replace event replay with snapshot delivery and introduces a new persistence query used by both shell and thread subscriptions. The focused tests reduce risk, but the autonomous gate and its customer-visible fallback behavior warrant human review.

You can add or adjust custom eligibility rules. Learn more.

rcawstonand others added 2 commits September 1, 2026 17:40
SQLite length() on TEXT counts code points, so non-ASCII payloads could
pass the 8 MiB budget at up to four times their byte size. Use
octet_length() and cover a multibyte row in the stats test.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@t3dotgg
t3dotggforce-pushed the fix/bound-orchestration-replay-payloads branch from c693c76 to adb6780CompareSeptember 2, 2026 00:46
@t3dotgg
t3dotgg merged commit 7e460f4 into pingdotgg:mainSep 2, 2026
20 checks passed
juliusmarminge added a commit that referenced this pull request Sep 2, 2026
Adopts main's round-19 features into the v2 stack: payload-budgeted
orchestration replay (#8992), sidebar row subscription leases (#9052),
tool group virtualization and scroll anchoring (#9106), repeated-command
and browser-group presentation, inline assistant citations (#9146),
per-cwd provider skills discovery (#8778), Claude composer skill
dispatch (#9128), grok health probe and model negotiation (#9154), and
the failed-tool thinking fallback (#9165).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
juliusmarminge added a commit that referenced this pull request Sep 2, 2026
Adopts main's round-19 features into the v2 stack: payload-budgeted
orchestration replay (#8992), sidebar row subscription leases (#9052),
tool group virtualization and scroll anchoring (#9106), repeated-command
and browser-group presentation, inline assistant citations (#9146),
per-cwd provider skills discovery (#8778), Claude composer skill
dispatch (#9128), grok health probe and model negotiation (#9154), and
the failed-tool thinking fallback (#9165).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:L100-499 changed lines (additions + deletions).vouch:unvouchedPR author is not yet trusted in the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@rcawston@t3dotgg
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

fix(server): bound orchestration replay payloads - #8992

Merged
t3dotgg merged 2 commits into
pingdotgg:mainfrom
rcawston:fix/bound-orchestration-replay-payloads
Sep 2, 2026
Merged

fix(server): bound orchestration replay payloads#8992
t3dotgg merged 2 commits into
pingdotgg:mainfrom
rcawston:fix/bound-orchestration-replay-payloads

Conversation

@rcawston

@rcawstonrcawston commented Sep 1, 2026

Copy link
Copy Markdown

What Changed

  • Add a lightweight replay-range query that counts events and serialized payload bytes without decoding them.
  • Fall back to a fresh snapshot when a reconnect gap exceeds the existing row limit or an 8 MiB payload budget.
  • Apply the same preflight behavior to thread and shell subscriptions.
  • Add focused coverage for oversized replay gaps and snapshot fallback behavior.

Why

Severity: high (regular-use blocker).

WebSocket catch-up checked only the number of missing events, then loaded and decoded the entire replay range before deciding whether it was safe to send. A small number of events can contain very large tool payloads, so reconnecting a client to a busy conversation could allocate multiple gigabytes, exhaust V8, restart the backend, and leave the client repeatedly reconnecting without a usable conversation.

Measuring the serialized range first lets the server choose the existing snapshot path before materializing unsafe payloads.

Checklist

  • This PR is small and focused
  • I explained what changed and why
  • I included before/after screenshots for any UI changes
  • I included a video for animation/interaction changes

Note

Bound WebSocket orchestration replay payloads to 8 MiB and add getEventReplayStats

  • Adds getEventReplayStats to the ProjectionSnapshotQuery service contract and SQL implementation, returning event count and serialized payload bytes for an exclusive-lower/inclusive-upper sequence range without decoding payloads.
  • Adds canReplayPersistedRange in ws.ts that rejects ranges ahead of the authoritative head, exceeding the event-gap limit, or exceeding the new 8 MiB serialized-payload budget.
  • Shell and thread subscription resume paths now call this helper before reading/decoding events; rejected ranges emit a fresh snapshot followed by synchronized/live updates.
  • Risk: subscriptions whose persisted ranges now exceed the 8 MiB budget or head/limit checks will silently switch from replay to snapshot delivery instead of replaying events — reviewers should check canReplayPersistedRange in ws.ts and the getEventReplayStats query in ProjectionSnapshotQuery.ts for boundary correctness.

Macroscope summarized adb6780.


Note

Medium Risk
Changes WebSocket reconnect/catch-up behavior for shell and thread subscriptions; incorrect byte accounting could still allow heavy replay or over-snapshot clients, but the path is defensive and well-tested.

Overview
WebSocket subscribeShell and subscribeThread catch-up used to allow replay whenever the sequence gap was within the existing ~1,000-event cap, which could still decode huge tool payloads and OOM the server.

This PR adds getEventReplayStats on ProjectionSnapshotQuery, aggregating event count and serialized payload_json size in SQL via octet_length (no payload decode). canReplayPersistedRange in ws.ts requires both the gap limit and an 8 MiB payload budget (ORCHESTRATION_REPLAY_PAYLOAD_BUDGET_BYTES); otherwise the client gets snapshot + synchronized instead of replay. Test mocks are updated, plus coverage for byte measurement (including emoji) and subscription fallback when stats exceed the budget.

Reviewed by Cursor Bugbot for commit adb6780. Bugbot is set up for automated code reviews on this repo. Configure here.

@github-actionsgithub-actionsBot added vouch:unvouched PR author is not yet trusted in the VOUCHED list. size:L 100-499 changed lines (additions + deletions). labels Sep 1, 2026
Comment threadapps/server/src/ws.ts
Comment threadapps/server/src/orchestration/Layers/ProjectionSnapshotQuery.ts Outdated
@macroscopeapp

macroscopeappBot commented Sep 1, 2026

Copy link
Copy Markdown
Contributor

Approvability

Verdict: Not approved

Macroscope's review found this PR not approvable — This changes production WebSocket reconnect behavior by adding an always-on 8 MiB policy that can replace event replay with snapshot delivery and introduces a new persistence query used by both shell and thread subscriptions. The focused tests reduce risk, but the autonomous gate and its customer-visible fallback behavior warrant human review.

You can add or adjust custom eligibility rules. Learn more.

rcawstonand others added 2 commits September 1, 2026 17:40
SQLite length() on TEXT counts code points, so non-ASCII payloads could
pass the 8 MiB budget at up to four times their byte size. Use
octet_length() and cover a multibyte row in the stats test.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@t3dotgg
t3dotggforce-pushed the fix/bound-orchestration-replay-payloads branch from c693c76 to adb6780CompareSeptember 2, 2026 00:46
@t3dotgg
t3dotgg merged commit 7e460f4 into pingdotgg:mainSep 2, 2026
20 checks passed
juliusmarminge added a commit that referenced this pull request Sep 2, 2026
Adopts main's round-19 features into the v2 stack: payload-budgeted
orchestration replay (#8992), sidebar row subscription leases (#9052),
tool group virtualization and scroll anchoring (#9106), repeated-command
and browser-group presentation, inline assistant citations (#9146),
per-cwd provider skills discovery (#8778), Claude composer skill
dispatch (#9128), grok health probe and model negotiation (#9154), and
the failed-tool thinking fallback (#9165).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
juliusmarminge added a commit that referenced this pull request Sep 2, 2026
Adopts main's round-19 features into the v2 stack: payload-budgeted
orchestration replay (#8992), sidebar row subscription leases (#9052),
tool group virtualization and scroll anchoring (#9106), repeated-command
and browser-group presentation, inline assistant citations (#9146),
per-cwd provider skills discovery (#8778), Claude composer skill
dispatch (#9128), grok health probe and model negotiation (#9154), and
the failed-tool thinking fallback (#9165).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:L100-499 changed lines (additions + deletions).vouch:unvouchedPR author is not yet trusted in the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

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

fix(server): bound orchestration replay payloads - #8992

Merged
t3dotgg merged 2 commits into
pingdotgg:mainfrom
rcawston:fix/bound-orchestration-replay-payloads
Sep 2, 2026
Merged

fix(server): bound orchestration replay payloads#8992
t3dotgg merged 2 commits into
pingdotgg:mainfrom
rcawston:fix/bound-orchestration-replay-payloads

Conversation

@rcawston

@rcawstonrcawston commented Sep 1, 2026

Copy link
Copy Markdown

What Changed

  • Add a lightweight replay-range query that counts events and serialized payload bytes without decoding them.
  • Fall back to a fresh snapshot when a reconnect gap exceeds the existing row limit or an 8 MiB payload budget.
  • Apply the same preflight behavior to thread and shell subscriptions.
  • Add focused coverage for oversized replay gaps and snapshot fallback behavior.

Why

Severity: high (regular-use blocker).

WebSocket catch-up checked only the number of missing events, then loaded and decoded the entire replay range before deciding whether it was safe to send. A small number of events can contain very large tool payloads, so reconnecting a client to a busy conversation could allocate multiple gigabytes, exhaust V8, restart the backend, and leave the client repeatedly reconnecting without a usable conversation.

Measuring the serialized range first lets the server choose the existing snapshot path before materializing unsafe payloads.

Checklist

  • This PR is small and focused
  • I explained what changed and why
  • I included before/after screenshots for any UI changes
  • I included a video for animation/interaction changes

Note

Bound WebSocket orchestration replay payloads to 8 MiB and add getEventReplayStats

  • Adds getEventReplayStats to the ProjectionSnapshotQuery service contract and SQL implementation, returning event count and serialized payload bytes for an exclusive-lower/inclusive-upper sequence range without decoding payloads.
  • Adds canReplayPersistedRange in ws.ts that rejects ranges ahead of the authoritative head, exceeding the event-gap limit, or exceeding the new 8 MiB serialized-payload budget.
  • Shell and thread subscription resume paths now call this helper before reading/decoding events; rejected ranges emit a fresh snapshot followed by synchronized/live updates.
  • Risk: subscriptions whose persisted ranges now exceed the 8 MiB budget or head/limit checks will silently switch from replay to snapshot delivery instead of replaying events — reviewers should check canReplayPersistedRange in ws.ts and the getEventReplayStats query in ProjectionSnapshotQuery.ts for boundary correctness.

Macroscope summarized adb6780.


Note

Medium Risk
Changes WebSocket reconnect/catch-up behavior for shell and thread subscriptions; incorrect byte accounting could still allow heavy replay or over-snapshot clients, but the path is defensive and well-tested.

Overview
WebSocket subscribeShell and subscribeThread catch-up used to allow replay whenever the sequence gap was within the existing ~1,000-event cap, which could still decode huge tool payloads and OOM the server.

This PR adds getEventReplayStats on ProjectionSnapshotQuery, aggregating event count and serialized payload_json size in SQL via octet_length (no payload decode). canReplayPersistedRange in ws.ts requires both the gap limit and an 8 MiB payload budget (ORCHESTRATION_REPLAY_PAYLOAD_BUDGET_BYTES); otherwise the client gets snapshot + synchronized instead of replay. Test mocks are updated, plus coverage for byte measurement (including emoji) and subscription fallback when stats exceed the budget.

Reviewed by Cursor Bugbot for commit adb6780. Bugbot is set up for automated code reviews on this repo. Configure here.

@github-actionsgithub-actionsBot added vouch:unvouched PR author is not yet trusted in the VOUCHED list. size:L 100-499 changed lines (additions + deletions). labels Sep 1, 2026
Comment threadapps/server/src/ws.ts
Comment threadapps/server/src/orchestration/Layers/ProjectionSnapshotQuery.ts Outdated
@macroscopeapp

macroscopeappBot commented Sep 1, 2026

Copy link
Copy Markdown
Contributor

Approvability

Verdict: Not approved

Macroscope's review found this PR not approvable — This changes production WebSocket reconnect behavior by adding an always-on 8 MiB policy that can replace event replay with snapshot delivery and introduces a new persistence query used by both shell and thread subscriptions. The focused tests reduce risk, but the autonomous gate and its customer-visible fallback behavior warrant human review.

You can add or adjust custom eligibility rules. Learn more.

rcawstonand others added 2 commits September 1, 2026 17:40
SQLite length() on TEXT counts code points, so non-ASCII payloads could
pass the 8 MiB budget at up to four times their byte size. Use
octet_length() and cover a multibyte row in the stats test.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@t3dotgg
t3dotggforce-pushed the fix/bound-orchestration-replay-payloads branch from c693c76 to adb6780CompareSeptember 2, 2026 00:46
@t3dotgg
t3dotgg merged commit 7e460f4 into pingdotgg:mainSep 2, 2026
20 checks passed
juliusmarminge added a commit that referenced this pull request Sep 2, 2026
Adopts main's round-19 features into the v2 stack: payload-budgeted
orchestration replay (#8992), sidebar row subscription leases (#9052),
tool group virtualization and scroll anchoring (#9106), repeated-command
and browser-group presentation, inline assistant citations (#9146),
per-cwd provider skills discovery (#8778), Claude composer skill
dispatch (#9128), grok health probe and model negotiation (#9154), and
the failed-tool thinking fallback (#9165).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
juliusmarminge added a commit that referenced this pull request Sep 2, 2026
Adopts main's round-19 features into the v2 stack: payload-budgeted
orchestration replay (#8992), sidebar row subscription leases (#9052),
tool group virtualization and scroll anchoring (#9106), repeated-command
and browser-group presentation, inline assistant citations (#9146),
per-cwd provider skills discovery (#8778), Claude composer skill
dispatch (#9128), grok health probe and model negotiation (#9154), and
the failed-tool thinking fallback (#9165).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:L100-499 changed lines (additions + deletions).vouch:unvouchedPR author is not yet trusted in the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@rcawston@t3dotgg