ci: add a manual disk-consumption stress workflow - #2408

Closed
aryanku-dev wants to merge 3 commits into
masterfrom
chore/stress-disk-workflow
Closed

ci: add a manual disk-consumption stress workflow#2408
aryanku-dev wants to merge 3 commits into
masterfrom
chore/stress-disk-workflow

Conversation

@aryanku-dev

Copy link
Copy Markdown
Contributor

Adds a workflow_dispatch-only job that measures how much scratch disk a large Percy snapshot build consumes on a stock CI runner.

Why

A local 250-page build exhausted a laptop and died with Browser not connected — not a disk error, which is what makes this worth catching. Measured rate was ~30 MB of scratch per snapshot, so:

build sizeprojected scratch
250 pages~7–8 GB
500 pages~15 GB

A stock ubuntu-latest runner has ~14 GB free. So a large Percy build plausibly cannot complete on a standard runner — and the failure mode is a confusing browser error rather than ENOSPC. This workflow answers that directly.

Leaked Chromium profile dirs ($TMPDIR/percy-browser-*, ~88 MB each, never cleaned up) accounted for only about a third of the local consumption. The rest is unexplained, which is the main reason for the 15s df/du sampler — it produces the timeline the local run could not.

Design notes

  • Manual trigger only. It consumes Percy quota (snapshots × browsers) and has no business running on push.
  • free_disk defaults to off, so the first run measures a stock runner. That is the customer-relevant number; enable it only to force a full-size run.
  • Needs a new PERCY_STRESS_TOKEN secret pointing at a throwaway project — deliberately notPERCY_REGRESSION_TOKEN, so stress builds never pollute the nightly regression baseline.
  • Inputs are passed through env: rather than interpolated into run: blocks.
  • Emits a step summary (snapshots taken, MB consumed, per-snapshot rate, whether the browser was lost) and uploads disk-timeline.csv + the Percy log as an artifact.

Merge note

workflow_dispatch workflows are only dispatchable once they exist on the default branch, so this has to merge before it can run.

Found while stress-testing the Node 20 CLI (#2386), but unrelated to that migration — the behaviour is not Node-version specific.

🤖 Generated with Claude Code

A local 250-page snapshot build exhausted a laptop's disk and died with
"Browser not connected" rather than a disk error, at roughly 30 MB of scratch
per snapshot. Leaked Chromium profile dirs explained only about a third of it,
so the rest is unaccounted for.
This runs the same build on a stock ubuntu runner and samples df/du every 15s,
producing the timeline the local run could not: where the bytes actually land,
and whether a large Percy build survives ~14 GB of runner disk at all. Both the
URL count and an optional runner-disk reclaim are inputs; reclaim defaults off
so the first run measures a stock runner, which is the customer-relevant case.
workflow_dispatch only — it consumes Percy quota (snapshots x browsers) and has
no business running on push. Needs a PERCY_STRESS_TOKEN secret pointing at a
throwaway project, deliberately not the nightly PERCY_REGRESSION_TOKEN.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@aryanku-dev
aryanku-dev requested a review from a team as a code ownerAugust 27, 2026 14:52
Comment thread.github/workflows/stress-disk.yml Fixed
Comment thread.github/workflows/stress-disk.yml Fixed
Comment thread.github/workflows/stress-disk.yml Fixed
aryanku-devand others added 2 commits August 27, 2026 20:44
workflow_dispatch is not dispatchable until the file reaches the default
branch (gh returned HTTP 404), so add a push trigger scoped to this branch —
the same mechanism the regression job uses — to get results before merge.
URL_COUNT falls back to 250 because `inputs` does not exist on push.
Both should come back out once this merges and dispatch works.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
semgrep's github-actions-mutable-action-tag rule blocked the branch on three
floating @v4 tags; every other workflow here pins to a full SHA, so match that
convention using the SHAs already in use. Verified locally with semgrep 1.173.0
(the version CI runs): 0 findings.
Also removes the push trigger now that it has served its purpose — run
33086755284 captured the pre-merge result. Dispatch-only from here, so this
push does not start another 250-URL build.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@aryanku-dev@github-advanced-security
, '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

ci: add a manual disk-consumption stress workflow - #2408

Closed
aryanku-dev wants to merge 3 commits into
masterfrom
chore/stress-disk-workflow
Closed

ci: add a manual disk-consumption stress workflow#2408
aryanku-dev wants to merge 3 commits into
masterfrom
chore/stress-disk-workflow

Conversation

@aryanku-dev

Copy link
Copy Markdown
Contributor

Adds a workflow_dispatch-only job that measures how much scratch disk a large Percy snapshot build consumes on a stock CI runner.

Why

A local 250-page build exhausted a laptop and died with Browser not connected — not a disk error, which is what makes this worth catching. Measured rate was ~30 MB of scratch per snapshot, so:

build sizeprojected scratch
250 pages~7–8 GB
500 pages~15 GB

A stock ubuntu-latest runner has ~14 GB free. So a large Percy build plausibly cannot complete on a standard runner — and the failure mode is a confusing browser error rather than ENOSPC. This workflow answers that directly.

Leaked Chromium profile dirs ($TMPDIR/percy-browser-*, ~88 MB each, never cleaned up) accounted for only about a third of the local consumption. The rest is unexplained, which is the main reason for the 15s df/du sampler — it produces the timeline the local run could not.

Design notes

  • Manual trigger only. It consumes Percy quota (snapshots × browsers) and has no business running on push.
  • free_disk defaults to off, so the first run measures a stock runner. That is the customer-relevant number; enable it only to force a full-size run.
  • Needs a new PERCY_STRESS_TOKEN secret pointing at a throwaway project — deliberately notPERCY_REGRESSION_TOKEN, so stress builds never pollute the nightly regression baseline.
  • Inputs are passed through env: rather than interpolated into run: blocks.
  • Emits a step summary (snapshots taken, MB consumed, per-snapshot rate, whether the browser was lost) and uploads disk-timeline.csv + the Percy log as an artifact.

Merge note

workflow_dispatch workflows are only dispatchable once they exist on the default branch, so this has to merge before it can run.

Found while stress-testing the Node 20 CLI (#2386), but unrelated to that migration — the behaviour is not Node-version specific.

🤖 Generated with Claude Code

A local 250-page snapshot build exhausted a laptop's disk and died with
"Browser not connected" rather than a disk error, at roughly 30 MB of scratch
per snapshot. Leaked Chromium profile dirs explained only about a third of it,
so the rest is unaccounted for.
This runs the same build on a stock ubuntu runner and samples df/du every 15s,
producing the timeline the local run could not: where the bytes actually land,
and whether a large Percy build survives ~14 GB of runner disk at all. Both the
URL count and an optional runner-disk reclaim are inputs; reclaim defaults off
so the first run measures a stock runner, which is the customer-relevant case.
workflow_dispatch only — it consumes Percy quota (snapshots x browsers) and has
no business running on push. Needs a PERCY_STRESS_TOKEN secret pointing at a
throwaway project, deliberately not the nightly PERCY_REGRESSION_TOKEN.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@aryanku-dev
aryanku-dev requested a review from a team as a code ownerAugust 27, 2026 14:52
Comment thread.github/workflows/stress-disk.yml Fixed
Comment thread.github/workflows/stress-disk.yml Fixed
Comment thread.github/workflows/stress-disk.yml Fixed
aryanku-devand others added 2 commits August 27, 2026 20:44
workflow_dispatch is not dispatchable until the file reaches the default
branch (gh returned HTTP 404), so add a push trigger scoped to this branch —
the same mechanism the regression job uses — to get results before merge.
URL_COUNT falls back to 250 because `inputs` does not exist on push.
Both should come back out once this merges and dispatch works.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
semgrep's github-actions-mutable-action-tag rule blocked the branch on three
floating @v4 tags; every other workflow here pins to a full SHA, so match that
convention using the SHAs already in use. Verified locally with semgrep 1.173.0
(the version CI runs): 0 findings.
Also removes the push trigger now that it has served its purpose — run
33086755284 captured the pre-merge result. Dispatch-only from here, so this
push does not start another 250-URL build.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@aryanku-dev@github-advanced-security
, '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

ci: add a manual disk-consumption stress workflow - #2408

Closed
aryanku-dev wants to merge 3 commits into
masterfrom
chore/stress-disk-workflow
Closed

ci: add a manual disk-consumption stress workflow#2408
aryanku-dev wants to merge 3 commits into
masterfrom
chore/stress-disk-workflow

Conversation

@aryanku-dev

Copy link
Copy Markdown
Contributor

Adds a workflow_dispatch-only job that measures how much scratch disk a large Percy snapshot build consumes on a stock CI runner.

Why

A local 250-page build exhausted a laptop and died with Browser not connected — not a disk error, which is what makes this worth catching. Measured rate was ~30 MB of scratch per snapshot, so:

build sizeprojected scratch
250 pages~7–8 GB
500 pages~15 GB

A stock ubuntu-latest runner has ~14 GB free. So a large Percy build plausibly cannot complete on a standard runner — and the failure mode is a confusing browser error rather than ENOSPC. This workflow answers that directly.

Leaked Chromium profile dirs ($TMPDIR/percy-browser-*, ~88 MB each, never cleaned up) accounted for only about a third of the local consumption. The rest is unexplained, which is the main reason for the 15s df/du sampler — it produces the timeline the local run could not.

Design notes

  • Manual trigger only. It consumes Percy quota (snapshots × browsers) and has no business running on push.
  • free_disk defaults to off, so the first run measures a stock runner. That is the customer-relevant number; enable it only to force a full-size run.
  • Needs a new PERCY_STRESS_TOKEN secret pointing at a throwaway project — deliberately notPERCY_REGRESSION_TOKEN, so stress builds never pollute the nightly regression baseline.
  • Inputs are passed through env: rather than interpolated into run: blocks.
  • Emits a step summary (snapshots taken, MB consumed, per-snapshot rate, whether the browser was lost) and uploads disk-timeline.csv + the Percy log as an artifact.

Merge note

workflow_dispatch workflows are only dispatchable once they exist on the default branch, so this has to merge before it can run.

Found while stress-testing the Node 20 CLI (#2386), but unrelated to that migration — the behaviour is not Node-version specific.

🤖 Generated with Claude Code

A local 250-page snapshot build exhausted a laptop's disk and died with
"Browser not connected" rather than a disk error, at roughly 30 MB of scratch
per snapshot. Leaked Chromium profile dirs explained only about a third of it,
so the rest is unaccounted for.
This runs the same build on a stock ubuntu runner and samples df/du every 15s,
producing the timeline the local run could not: where the bytes actually land,
and whether a large Percy build survives ~14 GB of runner disk at all. Both the
URL count and an optional runner-disk reclaim are inputs; reclaim defaults off
so the first run measures a stock runner, which is the customer-relevant case.
workflow_dispatch only — it consumes Percy quota (snapshots x browsers) and has
no business running on push. Needs a PERCY_STRESS_TOKEN secret pointing at a
throwaway project, deliberately not the nightly PERCY_REGRESSION_TOKEN.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@aryanku-dev
aryanku-dev requested a review from a team as a code ownerAugust 27, 2026 14:52
Comment thread.github/workflows/stress-disk.yml Fixed
Comment thread.github/workflows/stress-disk.yml Fixed
Comment thread.github/workflows/stress-disk.yml Fixed
aryanku-devand others added 2 commits August 27, 2026 20:44
workflow_dispatch is not dispatchable until the file reaches the default
branch (gh returned HTTP 404), so add a push trigger scoped to this branch —
the same mechanism the regression job uses — to get results before merge.
URL_COUNT falls back to 250 because `inputs` does not exist on push.
Both should come back out once this merges and dispatch works.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
semgrep's github-actions-mutable-action-tag rule blocked the branch on three
floating @v4 tags; every other workflow here pins to a full SHA, so match that
convention using the SHAs already in use. Verified locally with semgrep 1.173.0
(the version CI runs): 0 findings.
Also removes the push trigger now that it has served its purpose — run
33086755284 captured the pre-merge result. Dispatch-only from here, so this
push does not start another 250-URL build.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@aryanku-dev@github-advanced-security
, '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

ci: add a manual disk-consumption stress workflow - #2408

Closed
aryanku-dev wants to merge 3 commits into
masterfrom
chore/stress-disk-workflow
Closed

ci: add a manual disk-consumption stress workflow#2408
aryanku-dev wants to merge 3 commits into
masterfrom
chore/stress-disk-workflow

Conversation

@aryanku-dev

Copy link
Copy Markdown
Contributor

Adds a workflow_dispatch-only job that measures how much scratch disk a large Percy snapshot build consumes on a stock CI runner.

Why

A local 250-page build exhausted a laptop and died with Browser not connected — not a disk error, which is what makes this worth catching. Measured rate was ~30 MB of scratch per snapshot, so:

build sizeprojected scratch
250 pages~7–8 GB
500 pages~15 GB

A stock ubuntu-latest runner has ~14 GB free. So a large Percy build plausibly cannot complete on a standard runner — and the failure mode is a confusing browser error rather than ENOSPC. This workflow answers that directly.

Leaked Chromium profile dirs ($TMPDIR/percy-browser-*, ~88 MB each, never cleaned up) accounted for only about a third of the local consumption. The rest is unexplained, which is the main reason for the 15s df/du sampler — it produces the timeline the local run could not.

Design notes

  • Manual trigger only. It consumes Percy quota (snapshots × browsers) and has no business running on push.
  • free_disk defaults to off, so the first run measures a stock runner. That is the customer-relevant number; enable it only to force a full-size run.
  • Needs a new PERCY_STRESS_TOKEN secret pointing at a throwaway project — deliberately notPERCY_REGRESSION_TOKEN, so stress builds never pollute the nightly regression baseline.
  • Inputs are passed through env: rather than interpolated into run: blocks.
  • Emits a step summary (snapshots taken, MB consumed, per-snapshot rate, whether the browser was lost) and uploads disk-timeline.csv + the Percy log as an artifact.

Merge note

workflow_dispatch workflows are only dispatchable once they exist on the default branch, so this has to merge before it can run.

Found while stress-testing the Node 20 CLI (#2386), but unrelated to that migration — the behaviour is not Node-version specific.

🤖 Generated with Claude Code

A local 250-page snapshot build exhausted a laptop's disk and died with
"Browser not connected" rather than a disk error, at roughly 30 MB of scratch
per snapshot. Leaked Chromium profile dirs explained only about a third of it,
so the rest is unaccounted for.
This runs the same build on a stock ubuntu runner and samples df/du every 15s,
producing the timeline the local run could not: where the bytes actually land,
and whether a large Percy build survives ~14 GB of runner disk at all. Both the
URL count and an optional runner-disk reclaim are inputs; reclaim defaults off
so the first run measures a stock runner, which is the customer-relevant case.
workflow_dispatch only — it consumes Percy quota (snapshots x browsers) and has
no business running on push. Needs a PERCY_STRESS_TOKEN secret pointing at a
throwaway project, deliberately not the nightly PERCY_REGRESSION_TOKEN.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@aryanku-dev
aryanku-dev requested a review from a team as a code ownerAugust 27, 2026 14:52
Comment thread.github/workflows/stress-disk.yml Fixed
Comment thread.github/workflows/stress-disk.yml Fixed
Comment thread.github/workflows/stress-disk.yml Fixed
aryanku-devand others added 2 commits August 27, 2026 20:44
workflow_dispatch is not dispatchable until the file reaches the default
branch (gh returned HTTP 404), so add a push trigger scoped to this branch —
the same mechanism the regression job uses — to get results before merge.
URL_COUNT falls back to 250 because `inputs` does not exist on push.
Both should come back out once this merges and dispatch works.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
semgrep's github-actions-mutable-action-tag rule blocked the branch on three
floating @v4 tags; every other workflow here pins to a full SHA, so match that
convention using the SHAs already in use. Verified locally with semgrep 1.173.0
(the version CI runs): 0 findings.
Also removes the push trigger now that it has served its purpose — run
33086755284 captured the pre-merge result. Dispatch-only from here, so this
push does not start another 250-URL build.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@aryanku-dev@github-advanced-security
, '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

ci: add a manual disk-consumption stress workflow - #2408

Closed
aryanku-dev wants to merge 3 commits into
masterfrom
chore/stress-disk-workflow
Closed

ci: add a manual disk-consumption stress workflow#2408
aryanku-dev wants to merge 3 commits into
masterfrom
chore/stress-disk-workflow

Conversation

@aryanku-dev

Copy link
Copy Markdown
Contributor

Adds a workflow_dispatch-only job that measures how much scratch disk a large Percy snapshot build consumes on a stock CI runner.

Why

A local 250-page build exhausted a laptop and died with Browser not connected — not a disk error, which is what makes this worth catching. Measured rate was ~30 MB of scratch per snapshot, so:

build sizeprojected scratch
250 pages~7–8 GB
500 pages~15 GB

A stock ubuntu-latest runner has ~14 GB free. So a large Percy build plausibly cannot complete on a standard runner — and the failure mode is a confusing browser error rather than ENOSPC. This workflow answers that directly.

Leaked Chromium profile dirs ($TMPDIR/percy-browser-*, ~88 MB each, never cleaned up) accounted for only about a third of the local consumption. The rest is unexplained, which is the main reason for the 15s df/du sampler — it produces the timeline the local run could not.

Design notes

  • Manual trigger only. It consumes Percy quota (snapshots × browsers) and has no business running on push.
  • free_disk defaults to off, so the first run measures a stock runner. That is the customer-relevant number; enable it only to force a full-size run.
  • Needs a new PERCY_STRESS_TOKEN secret pointing at a throwaway project — deliberately notPERCY_REGRESSION_TOKEN, so stress builds never pollute the nightly regression baseline.
  • Inputs are passed through env: rather than interpolated into run: blocks.
  • Emits a step summary (snapshots taken, MB consumed, per-snapshot rate, whether the browser was lost) and uploads disk-timeline.csv + the Percy log as an artifact.

Merge note

workflow_dispatch workflows are only dispatchable once they exist on the default branch, so this has to merge before it can run.

Found while stress-testing the Node 20 CLI (#2386), but unrelated to that migration — the behaviour is not Node-version specific.

🤖 Generated with Claude Code

A local 250-page snapshot build exhausted a laptop's disk and died with
"Browser not connected" rather than a disk error, at roughly 30 MB of scratch
per snapshot. Leaked Chromium profile dirs explained only about a third of it,
so the rest is unaccounted for.
This runs the same build on a stock ubuntu runner and samples df/du every 15s,
producing the timeline the local run could not: where the bytes actually land,
and whether a large Percy build survives ~14 GB of runner disk at all. Both the
URL count and an optional runner-disk reclaim are inputs; reclaim defaults off
so the first run measures a stock runner, which is the customer-relevant case.
workflow_dispatch only — it consumes Percy quota (snapshots x browsers) and has
no business running on push. Needs a PERCY_STRESS_TOKEN secret pointing at a
throwaway project, deliberately not the nightly PERCY_REGRESSION_TOKEN.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@aryanku-dev
aryanku-dev requested a review from a team as a code ownerAugust 27, 2026 14:52
Comment thread.github/workflows/stress-disk.yml Fixed
Comment thread.github/workflows/stress-disk.yml Fixed
Comment thread.github/workflows/stress-disk.yml Fixed
aryanku-devand others added 2 commits August 27, 2026 20:44
workflow_dispatch is not dispatchable until the file reaches the default
branch (gh returned HTTP 404), so add a push trigger scoped to this branch —
the same mechanism the regression job uses — to get results before merge.
URL_COUNT falls back to 250 because `inputs` does not exist on push.
Both should come back out once this merges and dispatch works.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
semgrep's github-actions-mutable-action-tag rule blocked the branch on three
floating @v4 tags; every other workflow here pins to a full SHA, so match that
convention using the SHAs already in use. Verified locally with semgrep 1.173.0
(the version CI runs): 0 findings.
Also removes the push trigger now that it has served its purpose — run
33086755284 captured the pre-merge result. Dispatch-only from here, so this
push does not start another 250-URL build.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@aryanku-dev@github-advanced-security
, '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

ci: add a manual disk-consumption stress workflow - #2408

Closed
aryanku-dev wants to merge 3 commits into
masterfrom
chore/stress-disk-workflow
Closed

ci: add a manual disk-consumption stress workflow#2408
aryanku-dev wants to merge 3 commits into
masterfrom
chore/stress-disk-workflow

Conversation

@aryanku-dev

Copy link
Copy Markdown
Contributor

Adds a workflow_dispatch-only job that measures how much scratch disk a large Percy snapshot build consumes on a stock CI runner.

Why

A local 250-page build exhausted a laptop and died with Browser not connected — not a disk error, which is what makes this worth catching. Measured rate was ~30 MB of scratch per snapshot, so:

build sizeprojected scratch
250 pages~7–8 GB
500 pages~15 GB

A stock ubuntu-latest runner has ~14 GB free. So a large Percy build plausibly cannot complete on a standard runner — and the failure mode is a confusing browser error rather than ENOSPC. This workflow answers that directly.

Leaked Chromium profile dirs ($TMPDIR/percy-browser-*, ~88 MB each, never cleaned up) accounted for only about a third of the local consumption. The rest is unexplained, which is the main reason for the 15s df/du sampler — it produces the timeline the local run could not.

Design notes

  • Manual trigger only. It consumes Percy quota (snapshots × browsers) and has no business running on push.
  • free_disk defaults to off, so the first run measures a stock runner. That is the customer-relevant number; enable it only to force a full-size run.
  • Needs a new PERCY_STRESS_TOKEN secret pointing at a throwaway project — deliberately notPERCY_REGRESSION_TOKEN, so stress builds never pollute the nightly regression baseline.
  • Inputs are passed through env: rather than interpolated into run: blocks.
  • Emits a step summary (snapshots taken, MB consumed, per-snapshot rate, whether the browser was lost) and uploads disk-timeline.csv + the Percy log as an artifact.

Merge note

workflow_dispatch workflows are only dispatchable once they exist on the default branch, so this has to merge before it can run.

Found while stress-testing the Node 20 CLI (#2386), but unrelated to that migration — the behaviour is not Node-version specific.

🤖 Generated with Claude Code

A local 250-page snapshot build exhausted a laptop's disk and died with
"Browser not connected" rather than a disk error, at roughly 30 MB of scratch
per snapshot. Leaked Chromium profile dirs explained only about a third of it,
so the rest is unaccounted for.
This runs the same build on a stock ubuntu runner and samples df/du every 15s,
producing the timeline the local run could not: where the bytes actually land,
and whether a large Percy build survives ~14 GB of runner disk at all. Both the
URL count and an optional runner-disk reclaim are inputs; reclaim defaults off
so the first run measures a stock runner, which is the customer-relevant case.
workflow_dispatch only — it consumes Percy quota (snapshots x browsers) and has
no business running on push. Needs a PERCY_STRESS_TOKEN secret pointing at a
throwaway project, deliberately not the nightly PERCY_REGRESSION_TOKEN.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@aryanku-dev
aryanku-dev requested a review from a team as a code ownerAugust 27, 2026 14:52
Comment thread.github/workflows/stress-disk.yml Fixed
Comment thread.github/workflows/stress-disk.yml Fixed
Comment thread.github/workflows/stress-disk.yml Fixed
aryanku-devand others added 2 commits August 27, 2026 20:44
workflow_dispatch is not dispatchable until the file reaches the default
branch (gh returned HTTP 404), so add a push trigger scoped to this branch —
the same mechanism the regression job uses — to get results before merge.
URL_COUNT falls back to 250 because `inputs` does not exist on push.
Both should come back out once this merges and dispatch works.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
semgrep's github-actions-mutable-action-tag rule blocked the branch on three
floating @v4 tags; every other workflow here pins to a full SHA, so match that
convention using the SHAs already in use. Verified locally with semgrep 1.173.0
(the version CI runs): 0 findings.
Also removes the push trigger now that it has served its purpose — run
33086755284 captured the pre-merge result. Dispatch-only from here, so this
push does not start another 250-URL build.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@aryanku-dev@github-advanced-security
, '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

ci: add a manual disk-consumption stress workflow - #2408

Closed
aryanku-dev wants to merge 3 commits into
masterfrom
chore/stress-disk-workflow
Closed

ci: add a manual disk-consumption stress workflow#2408
aryanku-dev wants to merge 3 commits into
masterfrom
chore/stress-disk-workflow

Conversation

@aryanku-dev

Copy link
Copy Markdown
Contributor

Adds a workflow_dispatch-only job that measures how much scratch disk a large Percy snapshot build consumes on a stock CI runner.

Why

A local 250-page build exhausted a laptop and died with Browser not connected — not a disk error, which is what makes this worth catching. Measured rate was ~30 MB of scratch per snapshot, so:

build sizeprojected scratch
250 pages~7–8 GB
500 pages~15 GB

A stock ubuntu-latest runner has ~14 GB free. So a large Percy build plausibly cannot complete on a standard runner — and the failure mode is a confusing browser error rather than ENOSPC. This workflow answers that directly.

Leaked Chromium profile dirs ($TMPDIR/percy-browser-*, ~88 MB each, never cleaned up) accounted for only about a third of the local consumption. The rest is unexplained, which is the main reason for the 15s df/du sampler — it produces the timeline the local run could not.

Design notes

  • Manual trigger only. It consumes Percy quota (snapshots × browsers) and has no business running on push.
  • free_disk defaults to off, so the first run measures a stock runner. That is the customer-relevant number; enable it only to force a full-size run.
  • Needs a new PERCY_STRESS_TOKEN secret pointing at a throwaway project — deliberately notPERCY_REGRESSION_TOKEN, so stress builds never pollute the nightly regression baseline.
  • Inputs are passed through env: rather than interpolated into run: blocks.
  • Emits a step summary (snapshots taken, MB consumed, per-snapshot rate, whether the browser was lost) and uploads disk-timeline.csv + the Percy log as an artifact.

Merge note

workflow_dispatch workflows are only dispatchable once they exist on the default branch, so this has to merge before it can run.

Found while stress-testing the Node 20 CLI (#2386), but unrelated to that migration — the behaviour is not Node-version specific.

🤖 Generated with Claude Code

A local 250-page snapshot build exhausted a laptop's disk and died with
"Browser not connected" rather than a disk error, at roughly 30 MB of scratch
per snapshot. Leaked Chromium profile dirs explained only about a third of it,
so the rest is unaccounted for.
This runs the same build on a stock ubuntu runner and samples df/du every 15s,
producing the timeline the local run could not: where the bytes actually land,
and whether a large Percy build survives ~14 GB of runner disk at all. Both the
URL count and an optional runner-disk reclaim are inputs; reclaim defaults off
so the first run measures a stock runner, which is the customer-relevant case.
workflow_dispatch only — it consumes Percy quota (snapshots x browsers) and has
no business running on push. Needs a PERCY_STRESS_TOKEN secret pointing at a
throwaway project, deliberately not the nightly PERCY_REGRESSION_TOKEN.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@aryanku-dev
aryanku-dev requested a review from a team as a code ownerAugust 27, 2026 14:52
Comment thread.github/workflows/stress-disk.yml Fixed
Comment thread.github/workflows/stress-disk.yml Fixed
Comment thread.github/workflows/stress-disk.yml Fixed
aryanku-devand others added 2 commits August 27, 2026 20:44
workflow_dispatch is not dispatchable until the file reaches the default
branch (gh returned HTTP 404), so add a push trigger scoped to this branch —
the same mechanism the regression job uses — to get results before merge.
URL_COUNT falls back to 250 because `inputs` does not exist on push.
Both should come back out once this merges and dispatch works.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
semgrep's github-actions-mutable-action-tag rule blocked the branch on three
floating @v4 tags; every other workflow here pins to a full SHA, so match that
convention using the SHAs already in use. Verified locally with semgrep 1.173.0
(the version CI runs): 0 findings.
Also removes the push trigger now that it has served its purpose — run
33086755284 captured the pre-merge result. Dispatch-only from here, so this
push does not start another 250-URL build.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@aryanku-dev@github-advanced-security
, '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

ci: add a manual disk-consumption stress workflow - #2408

Closed
aryanku-dev wants to merge 3 commits into
masterfrom
chore/stress-disk-workflow
Closed

ci: add a manual disk-consumption stress workflow#2408
aryanku-dev wants to merge 3 commits into
masterfrom
chore/stress-disk-workflow

Conversation

@aryanku-dev

Copy link
Copy Markdown
Contributor

Adds a workflow_dispatch-only job that measures how much scratch disk a large Percy snapshot build consumes on a stock CI runner.

Why

A local 250-page build exhausted a laptop and died with Browser not connected — not a disk error, which is what makes this worth catching. Measured rate was ~30 MB of scratch per snapshot, so:

build sizeprojected scratch
250 pages~7–8 GB
500 pages~15 GB

A stock ubuntu-latest runner has ~14 GB free. So a large Percy build plausibly cannot complete on a standard runner — and the failure mode is a confusing browser error rather than ENOSPC. This workflow answers that directly.

Leaked Chromium profile dirs ($TMPDIR/percy-browser-*, ~88 MB each, never cleaned up) accounted for only about a third of the local consumption. The rest is unexplained, which is the main reason for the 15s df/du sampler — it produces the timeline the local run could not.

Design notes

  • Manual trigger only. It consumes Percy quota (snapshots × browsers) and has no business running on push.
  • free_disk defaults to off, so the first run measures a stock runner. That is the customer-relevant number; enable it only to force a full-size run.
  • Needs a new PERCY_STRESS_TOKEN secret pointing at a throwaway project — deliberately notPERCY_REGRESSION_TOKEN, so stress builds never pollute the nightly regression baseline.
  • Inputs are passed through env: rather than interpolated into run: blocks.
  • Emits a step summary (snapshots taken, MB consumed, per-snapshot rate, whether the browser was lost) and uploads disk-timeline.csv + the Percy log as an artifact.

Merge note

workflow_dispatch workflows are only dispatchable once they exist on the default branch, so this has to merge before it can run.

Found while stress-testing the Node 20 CLI (#2386), but unrelated to that migration — the behaviour is not Node-version specific.

🤖 Generated with Claude Code

A local 250-page snapshot build exhausted a laptop's disk and died with
"Browser not connected" rather than a disk error, at roughly 30 MB of scratch
per snapshot. Leaked Chromium profile dirs explained only about a third of it,
so the rest is unaccounted for.
This runs the same build on a stock ubuntu runner and samples df/du every 15s,
producing the timeline the local run could not: where the bytes actually land,
and whether a large Percy build survives ~14 GB of runner disk at all. Both the
URL count and an optional runner-disk reclaim are inputs; reclaim defaults off
so the first run measures a stock runner, which is the customer-relevant case.
workflow_dispatch only — it consumes Percy quota (snapshots x browsers) and has
no business running on push. Needs a PERCY_STRESS_TOKEN secret pointing at a
throwaway project, deliberately not the nightly PERCY_REGRESSION_TOKEN.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@aryanku-dev
aryanku-dev requested a review from a team as a code ownerAugust 27, 2026 14:52
Comment thread.github/workflows/stress-disk.yml Fixed
Comment thread.github/workflows/stress-disk.yml Fixed
Comment thread.github/workflows/stress-disk.yml Fixed
aryanku-devand others added 2 commits August 27, 2026 20:44
workflow_dispatch is not dispatchable until the file reaches the default
branch (gh returned HTTP 404), so add a push trigger scoped to this branch —
the same mechanism the regression job uses — to get results before merge.
URL_COUNT falls back to 250 because `inputs` does not exist on push.
Both should come back out once this merges and dispatch works.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
semgrep's github-actions-mutable-action-tag rule blocked the branch on three
floating @v4 tags; every other workflow here pins to a full SHA, so match that
convention using the SHAs already in use. Verified locally with semgrep 1.173.0
(the version CI runs): 0 findings.
Also removes the push trigger now that it has served its purpose — run
33086755284 captured the pre-merge result. Dispatch-only from here, so this
push does not start another 250-URL build.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@aryanku-dev@github-advanced-security