fix(server): bound thread activity hydration by bytes - #8387

Open
vidursaini wants to merge 1 commit into
pingdotgg:mainfrom
vidursaini:fix/bound-thread-activity-bytes
Open

fix(server): bound thread activity hydration by bytes#8387
vidursaini wants to merge 1 commit into
pingdotgg:mainfrom
vidursaini:fix/bound-thread-activity-bytes

Conversation

@vidursaini

@vidursainividursaini commented Aug 27, 2026

Copy link
Copy Markdown

What Changed

  • cap hydrated thread activity payloads at 16 MiB while retaining the existing 500-row limit
  • calculate the byte budget from the newest activity metadata before fetching payload_json into JavaScript
  • always retain the newest activity, even when that single payload exceeds the budget
  • apply the same rule to full and windowed thread detail reads
  • add focused coverage for byte-heavy activity histories; the existing unresolved approval and user-input coverage remains intact

Why

The 500-row cap added in #6153 bounds row count, but each payload_json can still contain megabytes of tool output. The server currently fetches and JSON-decodes those full payloads before #4622's client projection removes fields the clients do not read.

On an affected image-heavy thread, the newest 500 activities contained 140.58 MiB of JSON. The patched query selected the newest 96 activities within 15.72 MiB. In an isolated read-only database benchmark, RSS after querying and JSON-decoding fell from 332.0 MiB to 58.9 MiB. The patched SQL completed in 29 ms on that dataset.

This limits transient server heap pressure without deleting stored history, changing the wire contract, or adding a migration. Pending approvals and user-input requests are still loaded through the existing pinned-activity query.

Related: #4595, #5351, #6153, #4622

Validation

  • pnpm exec vp fmt --check apps/server/src/orchestration/Layers/ProjectionSnapshotQuery.ts apps/server/src/orchestration/Layers/ProjectionSnapshotQuery.test.ts
  • pnpm exec vp test run apps/server/src/orchestration/Layers/ProjectionSnapshotQuery.test.ts (22 passed)
  • cd apps/server && pnpm typecheck
  • git diff --check
  • isolated read-only benchmark against a production-sized SQLite snapshot

Checklist

  • This PR is small and focused
  • I explained what changed and why

Model: GPT-5.6-Sol. Harness: Codex desktop.


Note

Medium Risk
Changes thread detail hydration semantics (fewer activities may appear on image-heavy threads) but leaves stored history and the API contract intact; pinned approval/user-input paths are unchanged.

Overview
Thread detail reads now cap hydrated activity payload_json at 16 MiB in addition to the existing 500-row window, so megabyte tool outputs are not fully loaded and JSON-decoded in Node only to be trimmed later.

ProjectionSnapshotQuery rewrites full-thread and windowed activity SQL to score recent rows from metadata (LENGTH(payload_json)), walk newest-first with a running byte total, always keep the newest activity, then fetch full payloads only for rows inside the budget. The same rule applies to getThreadDetailById and windowed getThreadDetailSnapshot activity queries.

A new test seeds 18 ~1 MiB activities and asserts both detail paths return 15 activities (large-activity-0004 through large-activity-0018).

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

Note

Bound thread activity hydration by 16 MiB in ProjectionSnapshotQuery queries

  • Rewrites listThreadActivityRowsByThread and listThreadActivityRowsByThreadWindow with a CTE that computes cumulative serialized payload bytes and filters out older activities once the 16 MiB budget is exceeded.
  • The single most recent activity is always included even if it alone exceeds the budget.
  • Adds THREAD_DETAIL_ACTIVITY_BYTE_LIMIT (16 MiB) constant and a test seeding 18 large (~1 MiB) activities to verify only 15 are returned.
  • Behavioral Change: both listThreadActivityRowsByThread and listThreadActivityRowsByThreadWindow now return fewer activities when payloads are large; consumers expecting a fixed count may see reduced results.

Macroscope summarized 2cd55f1.

@coderabbitai

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: fa574845-d88e-4260-96e2-184788292bc0

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Comment @coderabbitai help to get the list of available commands.

@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 Aug 27, 2026
@macroscopeapp

Copy link
Copy Markdown
Contributor

Approvability

Verdict: Not approved

Macroscope's review found this PR not approvable — This changes existing full and windowed thread-detail responses by omitting older activities once serialized payloads exceed a 16 MiB hydration budget, while adding nontrivial SQL gating in both paths. The protection is focused and tested, but the runtime and response-semantic change merits human review.

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

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.

1 participant

@vidursaini
, '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 thread activity hydration by bytes - #8387

Open
vidursaini wants to merge 1 commit into
pingdotgg:mainfrom
vidursaini:fix/bound-thread-activity-bytes
Open

fix(server): bound thread activity hydration by bytes#8387
vidursaini wants to merge 1 commit into
pingdotgg:mainfrom
vidursaini:fix/bound-thread-activity-bytes

Conversation

@vidursaini

@vidursainividursaini commented Aug 27, 2026

Copy link
Copy Markdown

What Changed

  • cap hydrated thread activity payloads at 16 MiB while retaining the existing 500-row limit
  • calculate the byte budget from the newest activity metadata before fetching payload_json into JavaScript
  • always retain the newest activity, even when that single payload exceeds the budget
  • apply the same rule to full and windowed thread detail reads
  • add focused coverage for byte-heavy activity histories; the existing unresolved approval and user-input coverage remains intact

Why

The 500-row cap added in #6153 bounds row count, but each payload_json can still contain megabytes of tool output. The server currently fetches and JSON-decodes those full payloads before #4622's client projection removes fields the clients do not read.

On an affected image-heavy thread, the newest 500 activities contained 140.58 MiB of JSON. The patched query selected the newest 96 activities within 15.72 MiB. In an isolated read-only database benchmark, RSS after querying and JSON-decoding fell from 332.0 MiB to 58.9 MiB. The patched SQL completed in 29 ms on that dataset.

This limits transient server heap pressure without deleting stored history, changing the wire contract, or adding a migration. Pending approvals and user-input requests are still loaded through the existing pinned-activity query.

Related: #4595, #5351, #6153, #4622

Validation

  • pnpm exec vp fmt --check apps/server/src/orchestration/Layers/ProjectionSnapshotQuery.ts apps/server/src/orchestration/Layers/ProjectionSnapshotQuery.test.ts
  • pnpm exec vp test run apps/server/src/orchestration/Layers/ProjectionSnapshotQuery.test.ts (22 passed)
  • cd apps/server && pnpm typecheck
  • git diff --check
  • isolated read-only benchmark against a production-sized SQLite snapshot

Checklist

  • This PR is small and focused
  • I explained what changed and why

Model: GPT-5.6-Sol. Harness: Codex desktop.


Note

Medium Risk
Changes thread detail hydration semantics (fewer activities may appear on image-heavy threads) but leaves stored history and the API contract intact; pinned approval/user-input paths are unchanged.

Overview
Thread detail reads now cap hydrated activity payload_json at 16 MiB in addition to the existing 500-row window, so megabyte tool outputs are not fully loaded and JSON-decoded in Node only to be trimmed later.

ProjectionSnapshotQuery rewrites full-thread and windowed activity SQL to score recent rows from metadata (LENGTH(payload_json)), walk newest-first with a running byte total, always keep the newest activity, then fetch full payloads only for rows inside the budget. The same rule applies to getThreadDetailById and windowed getThreadDetailSnapshot activity queries.

A new test seeds 18 ~1 MiB activities and asserts both detail paths return 15 activities (large-activity-0004 through large-activity-0018).

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

Note

Bound thread activity hydration by 16 MiB in ProjectionSnapshotQuery queries

  • Rewrites listThreadActivityRowsByThread and listThreadActivityRowsByThreadWindow with a CTE that computes cumulative serialized payload bytes and filters out older activities once the 16 MiB budget is exceeded.
  • The single most recent activity is always included even if it alone exceeds the budget.
  • Adds THREAD_DETAIL_ACTIVITY_BYTE_LIMIT (16 MiB) constant and a test seeding 18 large (~1 MiB) activities to verify only 15 are returned.
  • Behavioral Change: both listThreadActivityRowsByThread and listThreadActivityRowsByThreadWindow now return fewer activities when payloads are large; consumers expecting a fixed count may see reduced results.

Macroscope summarized 2cd55f1.

@coderabbitai

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: fa574845-d88e-4260-96e2-184788292bc0

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Comment @coderabbitai help to get the list of available commands.

@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 Aug 27, 2026
@macroscopeapp

Copy link
Copy Markdown
Contributor

Approvability

Verdict: Not approved

Macroscope's review found this PR not approvable — This changes existing full and windowed thread-detail responses by omitting older activities once serialized payloads exceed a 16 MiB hydration budget, while adding nontrivial SQL gating in both paths. The protection is focused and tested, but the runtime and response-semantic change merits human review.

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

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.

1 participant

@vidursaini
, '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 thread activity hydration by bytes - #8387

Open
vidursaini wants to merge 1 commit into
pingdotgg:mainfrom
vidursaini:fix/bound-thread-activity-bytes
Open

fix(server): bound thread activity hydration by bytes#8387
vidursaini wants to merge 1 commit into
pingdotgg:mainfrom
vidursaini:fix/bound-thread-activity-bytes

Conversation

@vidursaini

@vidursainividursaini commented Aug 27, 2026

Copy link
Copy Markdown

What Changed

  • cap hydrated thread activity payloads at 16 MiB while retaining the existing 500-row limit
  • calculate the byte budget from the newest activity metadata before fetching payload_json into JavaScript
  • always retain the newest activity, even when that single payload exceeds the budget
  • apply the same rule to full and windowed thread detail reads
  • add focused coverage for byte-heavy activity histories; the existing unresolved approval and user-input coverage remains intact

Why

The 500-row cap added in #6153 bounds row count, but each payload_json can still contain megabytes of tool output. The server currently fetches and JSON-decodes those full payloads before #4622's client projection removes fields the clients do not read.

On an affected image-heavy thread, the newest 500 activities contained 140.58 MiB of JSON. The patched query selected the newest 96 activities within 15.72 MiB. In an isolated read-only database benchmark, RSS after querying and JSON-decoding fell from 332.0 MiB to 58.9 MiB. The patched SQL completed in 29 ms on that dataset.

This limits transient server heap pressure without deleting stored history, changing the wire contract, or adding a migration. Pending approvals and user-input requests are still loaded through the existing pinned-activity query.

Related: #4595, #5351, #6153, #4622

Validation

  • pnpm exec vp fmt --check apps/server/src/orchestration/Layers/ProjectionSnapshotQuery.ts apps/server/src/orchestration/Layers/ProjectionSnapshotQuery.test.ts
  • pnpm exec vp test run apps/server/src/orchestration/Layers/ProjectionSnapshotQuery.test.ts (22 passed)
  • cd apps/server && pnpm typecheck
  • git diff --check
  • isolated read-only benchmark against a production-sized SQLite snapshot

Checklist

  • This PR is small and focused
  • I explained what changed and why

Model: GPT-5.6-Sol. Harness: Codex desktop.


Note

Medium Risk
Changes thread detail hydration semantics (fewer activities may appear on image-heavy threads) but leaves stored history and the API contract intact; pinned approval/user-input paths are unchanged.

Overview
Thread detail reads now cap hydrated activity payload_json at 16 MiB in addition to the existing 500-row window, so megabyte tool outputs are not fully loaded and JSON-decoded in Node only to be trimmed later.

ProjectionSnapshotQuery rewrites full-thread and windowed activity SQL to score recent rows from metadata (LENGTH(payload_json)), walk newest-first with a running byte total, always keep the newest activity, then fetch full payloads only for rows inside the budget. The same rule applies to getThreadDetailById and windowed getThreadDetailSnapshot activity queries.

A new test seeds 18 ~1 MiB activities and asserts both detail paths return 15 activities (large-activity-0004 through large-activity-0018).

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

Note

Bound thread activity hydration by 16 MiB in ProjectionSnapshotQuery queries

  • Rewrites listThreadActivityRowsByThread and listThreadActivityRowsByThreadWindow with a CTE that computes cumulative serialized payload bytes and filters out older activities once the 16 MiB budget is exceeded.
  • The single most recent activity is always included even if it alone exceeds the budget.
  • Adds THREAD_DETAIL_ACTIVITY_BYTE_LIMIT (16 MiB) constant and a test seeding 18 large (~1 MiB) activities to verify only 15 are returned.
  • Behavioral Change: both listThreadActivityRowsByThread and listThreadActivityRowsByThreadWindow now return fewer activities when payloads are large; consumers expecting a fixed count may see reduced results.

Macroscope summarized 2cd55f1.

@coderabbitai

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: fa574845-d88e-4260-96e2-184788292bc0

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Comment @coderabbitai help to get the list of available commands.

@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 Aug 27, 2026
@macroscopeapp

Copy link
Copy Markdown
Contributor

Approvability

Verdict: Not approved

Macroscope's review found this PR not approvable — This changes existing full and windowed thread-detail responses by omitting older activities once serialized payloads exceed a 16 MiB hydration budget, while adding nontrivial SQL gating in both paths. The protection is focused and tested, but the runtime and response-semantic change merits human review.

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

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.

1 participant

@vidursaini
, '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 thread activity hydration by bytes - #8387

Open
vidursaini wants to merge 1 commit into
pingdotgg:mainfrom
vidursaini:fix/bound-thread-activity-bytes
Open

fix(server): bound thread activity hydration by bytes#8387
vidursaini wants to merge 1 commit into
pingdotgg:mainfrom
vidursaini:fix/bound-thread-activity-bytes

Conversation

@vidursaini

@vidursainividursaini commented Aug 27, 2026

Copy link
Copy Markdown

What Changed

  • cap hydrated thread activity payloads at 16 MiB while retaining the existing 500-row limit
  • calculate the byte budget from the newest activity metadata before fetching payload_json into JavaScript
  • always retain the newest activity, even when that single payload exceeds the budget
  • apply the same rule to full and windowed thread detail reads
  • add focused coverage for byte-heavy activity histories; the existing unresolved approval and user-input coverage remains intact

Why

The 500-row cap added in #6153 bounds row count, but each payload_json can still contain megabytes of tool output. The server currently fetches and JSON-decodes those full payloads before #4622's client projection removes fields the clients do not read.

On an affected image-heavy thread, the newest 500 activities contained 140.58 MiB of JSON. The patched query selected the newest 96 activities within 15.72 MiB. In an isolated read-only database benchmark, RSS after querying and JSON-decoding fell from 332.0 MiB to 58.9 MiB. The patched SQL completed in 29 ms on that dataset.

This limits transient server heap pressure without deleting stored history, changing the wire contract, or adding a migration. Pending approvals and user-input requests are still loaded through the existing pinned-activity query.

Related: #4595, #5351, #6153, #4622

Validation

  • pnpm exec vp fmt --check apps/server/src/orchestration/Layers/ProjectionSnapshotQuery.ts apps/server/src/orchestration/Layers/ProjectionSnapshotQuery.test.ts
  • pnpm exec vp test run apps/server/src/orchestration/Layers/ProjectionSnapshotQuery.test.ts (22 passed)
  • cd apps/server && pnpm typecheck
  • git diff --check
  • isolated read-only benchmark against a production-sized SQLite snapshot

Checklist

  • This PR is small and focused
  • I explained what changed and why

Model: GPT-5.6-Sol. Harness: Codex desktop.


Note

Medium Risk
Changes thread detail hydration semantics (fewer activities may appear on image-heavy threads) but leaves stored history and the API contract intact; pinned approval/user-input paths are unchanged.

Overview
Thread detail reads now cap hydrated activity payload_json at 16 MiB in addition to the existing 500-row window, so megabyte tool outputs are not fully loaded and JSON-decoded in Node only to be trimmed later.

ProjectionSnapshotQuery rewrites full-thread and windowed activity SQL to score recent rows from metadata (LENGTH(payload_json)), walk newest-first with a running byte total, always keep the newest activity, then fetch full payloads only for rows inside the budget. The same rule applies to getThreadDetailById and windowed getThreadDetailSnapshot activity queries.

A new test seeds 18 ~1 MiB activities and asserts both detail paths return 15 activities (large-activity-0004 through large-activity-0018).

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

Note

Bound thread activity hydration by 16 MiB in ProjectionSnapshotQuery queries

  • Rewrites listThreadActivityRowsByThread and listThreadActivityRowsByThreadWindow with a CTE that computes cumulative serialized payload bytes and filters out older activities once the 16 MiB budget is exceeded.
  • The single most recent activity is always included even if it alone exceeds the budget.
  • Adds THREAD_DETAIL_ACTIVITY_BYTE_LIMIT (16 MiB) constant and a test seeding 18 large (~1 MiB) activities to verify only 15 are returned.
  • Behavioral Change: both listThreadActivityRowsByThread and listThreadActivityRowsByThreadWindow now return fewer activities when payloads are large; consumers expecting a fixed count may see reduced results.

Macroscope summarized 2cd55f1.

@coderabbitai

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: fa574845-d88e-4260-96e2-184788292bc0

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Comment @coderabbitai help to get the list of available commands.

@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 Aug 27, 2026
@macroscopeapp

Copy link
Copy Markdown
Contributor

Approvability

Verdict: Not approved

Macroscope's review found this PR not approvable — This changes existing full and windowed thread-detail responses by omitting older activities once serialized payloads exceed a 16 MiB hydration budget, while adding nontrivial SQL gating in both paths. The protection is focused and tested, but the runtime and response-semantic change merits human review.

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

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.

1 participant

@vidursaini
, '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 thread activity hydration by bytes - #8387

Open
vidursaini wants to merge 1 commit into
pingdotgg:mainfrom
vidursaini:fix/bound-thread-activity-bytes
Open

fix(server): bound thread activity hydration by bytes#8387
vidursaini wants to merge 1 commit into
pingdotgg:mainfrom
vidursaini:fix/bound-thread-activity-bytes

Conversation

@vidursaini

@vidursainividursaini commented Aug 27, 2026

Copy link
Copy Markdown

What Changed

  • cap hydrated thread activity payloads at 16 MiB while retaining the existing 500-row limit
  • calculate the byte budget from the newest activity metadata before fetching payload_json into JavaScript
  • always retain the newest activity, even when that single payload exceeds the budget
  • apply the same rule to full and windowed thread detail reads
  • add focused coverage for byte-heavy activity histories; the existing unresolved approval and user-input coverage remains intact

Why

The 500-row cap added in #6153 bounds row count, but each payload_json can still contain megabytes of tool output. The server currently fetches and JSON-decodes those full payloads before #4622's client projection removes fields the clients do not read.

On an affected image-heavy thread, the newest 500 activities contained 140.58 MiB of JSON. The patched query selected the newest 96 activities within 15.72 MiB. In an isolated read-only database benchmark, RSS after querying and JSON-decoding fell from 332.0 MiB to 58.9 MiB. The patched SQL completed in 29 ms on that dataset.

This limits transient server heap pressure without deleting stored history, changing the wire contract, or adding a migration. Pending approvals and user-input requests are still loaded through the existing pinned-activity query.

Related: #4595, #5351, #6153, #4622

Validation

  • pnpm exec vp fmt --check apps/server/src/orchestration/Layers/ProjectionSnapshotQuery.ts apps/server/src/orchestration/Layers/ProjectionSnapshotQuery.test.ts
  • pnpm exec vp test run apps/server/src/orchestration/Layers/ProjectionSnapshotQuery.test.ts (22 passed)
  • cd apps/server && pnpm typecheck
  • git diff --check
  • isolated read-only benchmark against a production-sized SQLite snapshot

Checklist

  • This PR is small and focused
  • I explained what changed and why

Model: GPT-5.6-Sol. Harness: Codex desktop.


Note

Medium Risk
Changes thread detail hydration semantics (fewer activities may appear on image-heavy threads) but leaves stored history and the API contract intact; pinned approval/user-input paths are unchanged.

Overview
Thread detail reads now cap hydrated activity payload_json at 16 MiB in addition to the existing 500-row window, so megabyte tool outputs are not fully loaded and JSON-decoded in Node only to be trimmed later.

ProjectionSnapshotQuery rewrites full-thread and windowed activity SQL to score recent rows from metadata (LENGTH(payload_json)), walk newest-first with a running byte total, always keep the newest activity, then fetch full payloads only for rows inside the budget. The same rule applies to getThreadDetailById and windowed getThreadDetailSnapshot activity queries.

A new test seeds 18 ~1 MiB activities and asserts both detail paths return 15 activities (large-activity-0004 through large-activity-0018).

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

Note

Bound thread activity hydration by 16 MiB in ProjectionSnapshotQuery queries

  • Rewrites listThreadActivityRowsByThread and listThreadActivityRowsByThreadWindow with a CTE that computes cumulative serialized payload bytes and filters out older activities once the 16 MiB budget is exceeded.
  • The single most recent activity is always included even if it alone exceeds the budget.
  • Adds THREAD_DETAIL_ACTIVITY_BYTE_LIMIT (16 MiB) constant and a test seeding 18 large (~1 MiB) activities to verify only 15 are returned.
  • Behavioral Change: both listThreadActivityRowsByThread and listThreadActivityRowsByThreadWindow now return fewer activities when payloads are large; consumers expecting a fixed count may see reduced results.

Macroscope summarized 2cd55f1.

@coderabbitai

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: fa574845-d88e-4260-96e2-184788292bc0

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Comment @coderabbitai help to get the list of available commands.

@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 Aug 27, 2026
@macroscopeapp

Copy link
Copy Markdown
Contributor

Approvability

Verdict: Not approved

Macroscope's review found this PR not approvable — This changes existing full and windowed thread-detail responses by omitting older activities once serialized payloads exceed a 16 MiB hydration budget, while adding nontrivial SQL gating in both paths. The protection is focused and tested, but the runtime and response-semantic change merits human review.

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

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.

1 participant

@vidursaini
, '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 thread activity hydration by bytes - #8387

Open
vidursaini wants to merge 1 commit into
pingdotgg:mainfrom
vidursaini:fix/bound-thread-activity-bytes
Open

fix(server): bound thread activity hydration by bytes#8387
vidursaini wants to merge 1 commit into
pingdotgg:mainfrom
vidursaini:fix/bound-thread-activity-bytes

Conversation

@vidursaini

@vidursainividursaini commented Aug 27, 2026

Copy link
Copy Markdown

What Changed

  • cap hydrated thread activity payloads at 16 MiB while retaining the existing 500-row limit
  • calculate the byte budget from the newest activity metadata before fetching payload_json into JavaScript
  • always retain the newest activity, even when that single payload exceeds the budget
  • apply the same rule to full and windowed thread detail reads
  • add focused coverage for byte-heavy activity histories; the existing unresolved approval and user-input coverage remains intact

Why

The 500-row cap added in #6153 bounds row count, but each payload_json can still contain megabytes of tool output. The server currently fetches and JSON-decodes those full payloads before #4622's client projection removes fields the clients do not read.

On an affected image-heavy thread, the newest 500 activities contained 140.58 MiB of JSON. The patched query selected the newest 96 activities within 15.72 MiB. In an isolated read-only database benchmark, RSS after querying and JSON-decoding fell from 332.0 MiB to 58.9 MiB. The patched SQL completed in 29 ms on that dataset.

This limits transient server heap pressure without deleting stored history, changing the wire contract, or adding a migration. Pending approvals and user-input requests are still loaded through the existing pinned-activity query.

Related: #4595, #5351, #6153, #4622

Validation

  • pnpm exec vp fmt --check apps/server/src/orchestration/Layers/ProjectionSnapshotQuery.ts apps/server/src/orchestration/Layers/ProjectionSnapshotQuery.test.ts
  • pnpm exec vp test run apps/server/src/orchestration/Layers/ProjectionSnapshotQuery.test.ts (22 passed)
  • cd apps/server && pnpm typecheck
  • git diff --check
  • isolated read-only benchmark against a production-sized SQLite snapshot

Checklist

  • This PR is small and focused
  • I explained what changed and why

Model: GPT-5.6-Sol. Harness: Codex desktop.


Note

Medium Risk
Changes thread detail hydration semantics (fewer activities may appear on image-heavy threads) but leaves stored history and the API contract intact; pinned approval/user-input paths are unchanged.

Overview
Thread detail reads now cap hydrated activity payload_json at 16 MiB in addition to the existing 500-row window, so megabyte tool outputs are not fully loaded and JSON-decoded in Node only to be trimmed later.

ProjectionSnapshotQuery rewrites full-thread and windowed activity SQL to score recent rows from metadata (LENGTH(payload_json)), walk newest-first with a running byte total, always keep the newest activity, then fetch full payloads only for rows inside the budget. The same rule applies to getThreadDetailById and windowed getThreadDetailSnapshot activity queries.

A new test seeds 18 ~1 MiB activities and asserts both detail paths return 15 activities (large-activity-0004 through large-activity-0018).

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

Note

Bound thread activity hydration by 16 MiB in ProjectionSnapshotQuery queries

  • Rewrites listThreadActivityRowsByThread and listThreadActivityRowsByThreadWindow with a CTE that computes cumulative serialized payload bytes and filters out older activities once the 16 MiB budget is exceeded.
  • The single most recent activity is always included even if it alone exceeds the budget.
  • Adds THREAD_DETAIL_ACTIVITY_BYTE_LIMIT (16 MiB) constant and a test seeding 18 large (~1 MiB) activities to verify only 15 are returned.
  • Behavioral Change: both listThreadActivityRowsByThread and listThreadActivityRowsByThreadWindow now return fewer activities when payloads are large; consumers expecting a fixed count may see reduced results.

Macroscope summarized 2cd55f1.

@coderabbitai

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: fa574845-d88e-4260-96e2-184788292bc0

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Comment @coderabbitai help to get the list of available commands.

@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 Aug 27, 2026
@macroscopeapp

Copy link
Copy Markdown
Contributor

Approvability

Verdict: Not approved

Macroscope's review found this PR not approvable — This changes existing full and windowed thread-detail responses by omitting older activities once serialized payloads exceed a 16 MiB hydration budget, while adding nontrivial SQL gating in both paths. The protection is focused and tested, but the runtime and response-semantic change merits human review.

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

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.

1 participant

@vidursaini
, '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 thread activity hydration by bytes - #8387

Open
vidursaini wants to merge 1 commit into
pingdotgg:mainfrom
vidursaini:fix/bound-thread-activity-bytes
Open

fix(server): bound thread activity hydration by bytes#8387
vidursaini wants to merge 1 commit into
pingdotgg:mainfrom
vidursaini:fix/bound-thread-activity-bytes

Conversation

@vidursaini

@vidursainividursaini commented Aug 27, 2026

Copy link
Copy Markdown

What Changed

  • cap hydrated thread activity payloads at 16 MiB while retaining the existing 500-row limit
  • calculate the byte budget from the newest activity metadata before fetching payload_json into JavaScript
  • always retain the newest activity, even when that single payload exceeds the budget
  • apply the same rule to full and windowed thread detail reads
  • add focused coverage for byte-heavy activity histories; the existing unresolved approval and user-input coverage remains intact

Why

The 500-row cap added in #6153 bounds row count, but each payload_json can still contain megabytes of tool output. The server currently fetches and JSON-decodes those full payloads before #4622's client projection removes fields the clients do not read.

On an affected image-heavy thread, the newest 500 activities contained 140.58 MiB of JSON. The patched query selected the newest 96 activities within 15.72 MiB. In an isolated read-only database benchmark, RSS after querying and JSON-decoding fell from 332.0 MiB to 58.9 MiB. The patched SQL completed in 29 ms on that dataset.

This limits transient server heap pressure without deleting stored history, changing the wire contract, or adding a migration. Pending approvals and user-input requests are still loaded through the existing pinned-activity query.

Related: #4595, #5351, #6153, #4622

Validation

  • pnpm exec vp fmt --check apps/server/src/orchestration/Layers/ProjectionSnapshotQuery.ts apps/server/src/orchestration/Layers/ProjectionSnapshotQuery.test.ts
  • pnpm exec vp test run apps/server/src/orchestration/Layers/ProjectionSnapshotQuery.test.ts (22 passed)
  • cd apps/server && pnpm typecheck
  • git diff --check
  • isolated read-only benchmark against a production-sized SQLite snapshot

Checklist

  • This PR is small and focused
  • I explained what changed and why

Model: GPT-5.6-Sol. Harness: Codex desktop.


Note

Medium Risk
Changes thread detail hydration semantics (fewer activities may appear on image-heavy threads) but leaves stored history and the API contract intact; pinned approval/user-input paths are unchanged.

Overview
Thread detail reads now cap hydrated activity payload_json at 16 MiB in addition to the existing 500-row window, so megabyte tool outputs are not fully loaded and JSON-decoded in Node only to be trimmed later.

ProjectionSnapshotQuery rewrites full-thread and windowed activity SQL to score recent rows from metadata (LENGTH(payload_json)), walk newest-first with a running byte total, always keep the newest activity, then fetch full payloads only for rows inside the budget. The same rule applies to getThreadDetailById and windowed getThreadDetailSnapshot activity queries.

A new test seeds 18 ~1 MiB activities and asserts both detail paths return 15 activities (large-activity-0004 through large-activity-0018).

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

Note

Bound thread activity hydration by 16 MiB in ProjectionSnapshotQuery queries

  • Rewrites listThreadActivityRowsByThread and listThreadActivityRowsByThreadWindow with a CTE that computes cumulative serialized payload bytes and filters out older activities once the 16 MiB budget is exceeded.
  • The single most recent activity is always included even if it alone exceeds the budget.
  • Adds THREAD_DETAIL_ACTIVITY_BYTE_LIMIT (16 MiB) constant and a test seeding 18 large (~1 MiB) activities to verify only 15 are returned.
  • Behavioral Change: both listThreadActivityRowsByThread and listThreadActivityRowsByThreadWindow now return fewer activities when payloads are large; consumers expecting a fixed count may see reduced results.

Macroscope summarized 2cd55f1.

@coderabbitai

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: fa574845-d88e-4260-96e2-184788292bc0

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Comment @coderabbitai help to get the list of available commands.

@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 Aug 27, 2026
@macroscopeapp

Copy link
Copy Markdown
Contributor

Approvability

Verdict: Not approved

Macroscope's review found this PR not approvable — This changes existing full and windowed thread-detail responses by omitting older activities once serialized payloads exceed a 16 MiB hydration budget, while adding nontrivial SQL gating in both paths. The protection is focused and tested, but the runtime and response-semantic change merits human review.

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

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.

1 participant

@vidursaini
, '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 thread activity hydration by bytes - #8387

Open
vidursaini wants to merge 1 commit into
pingdotgg:mainfrom
vidursaini:fix/bound-thread-activity-bytes
Open

fix(server): bound thread activity hydration by bytes#8387
vidursaini wants to merge 1 commit into
pingdotgg:mainfrom
vidursaini:fix/bound-thread-activity-bytes

Conversation

@vidursaini

@vidursainividursaini commented Aug 27, 2026

Copy link
Copy Markdown

What Changed

  • cap hydrated thread activity payloads at 16 MiB while retaining the existing 500-row limit
  • calculate the byte budget from the newest activity metadata before fetching payload_json into JavaScript
  • always retain the newest activity, even when that single payload exceeds the budget
  • apply the same rule to full and windowed thread detail reads
  • add focused coverage for byte-heavy activity histories; the existing unresolved approval and user-input coverage remains intact

Why

The 500-row cap added in #6153 bounds row count, but each payload_json can still contain megabytes of tool output. The server currently fetches and JSON-decodes those full payloads before #4622's client projection removes fields the clients do not read.

On an affected image-heavy thread, the newest 500 activities contained 140.58 MiB of JSON. The patched query selected the newest 96 activities within 15.72 MiB. In an isolated read-only database benchmark, RSS after querying and JSON-decoding fell from 332.0 MiB to 58.9 MiB. The patched SQL completed in 29 ms on that dataset.

This limits transient server heap pressure without deleting stored history, changing the wire contract, or adding a migration. Pending approvals and user-input requests are still loaded through the existing pinned-activity query.

Related: #4595, #5351, #6153, #4622

Validation

  • pnpm exec vp fmt --check apps/server/src/orchestration/Layers/ProjectionSnapshotQuery.ts apps/server/src/orchestration/Layers/ProjectionSnapshotQuery.test.ts
  • pnpm exec vp test run apps/server/src/orchestration/Layers/ProjectionSnapshotQuery.test.ts (22 passed)
  • cd apps/server && pnpm typecheck
  • git diff --check
  • isolated read-only benchmark against a production-sized SQLite snapshot

Checklist

  • This PR is small and focused
  • I explained what changed and why

Model: GPT-5.6-Sol. Harness: Codex desktop.


Note

Medium Risk
Changes thread detail hydration semantics (fewer activities may appear on image-heavy threads) but leaves stored history and the API contract intact; pinned approval/user-input paths are unchanged.

Overview
Thread detail reads now cap hydrated activity payload_json at 16 MiB in addition to the existing 500-row window, so megabyte tool outputs are not fully loaded and JSON-decoded in Node only to be trimmed later.

ProjectionSnapshotQuery rewrites full-thread and windowed activity SQL to score recent rows from metadata (LENGTH(payload_json)), walk newest-first with a running byte total, always keep the newest activity, then fetch full payloads only for rows inside the budget. The same rule applies to getThreadDetailById and windowed getThreadDetailSnapshot activity queries.

A new test seeds 18 ~1 MiB activities and asserts both detail paths return 15 activities (large-activity-0004 through large-activity-0018).

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

Note

Bound thread activity hydration by 16 MiB in ProjectionSnapshotQuery queries

  • Rewrites listThreadActivityRowsByThread and listThreadActivityRowsByThreadWindow with a CTE that computes cumulative serialized payload bytes and filters out older activities once the 16 MiB budget is exceeded.
  • The single most recent activity is always included even if it alone exceeds the budget.
  • Adds THREAD_DETAIL_ACTIVITY_BYTE_LIMIT (16 MiB) constant and a test seeding 18 large (~1 MiB) activities to verify only 15 are returned.
  • Behavioral Change: both listThreadActivityRowsByThread and listThreadActivityRowsByThreadWindow now return fewer activities when payloads are large; consumers expecting a fixed count may see reduced results.

Macroscope summarized 2cd55f1.

@coderabbitai

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: fa574845-d88e-4260-96e2-184788292bc0

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Comment @coderabbitai help to get the list of available commands.

@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 Aug 27, 2026
@macroscopeapp

Copy link
Copy Markdown
Contributor

Approvability

Verdict: Not approved

Macroscope's review found this PR not approvable — This changes existing full and windowed thread-detail responses by omitting older activities once serialized payloads exceed a 16 MiB hydration budget, while adding nontrivial SQL gating in both paths. The protection is focused and tested, but the runtime and response-semantic change merits human review.

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

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.

1 participant

@vidursaini