fix(server): report incomplete usage scans - #5812

Open
caezium wants to merge 1 commit into
pingdotgg:mainfrom
caezium:agent/usage-scan-errors
Open

fix(server): report incomplete usage scans#5812
caezium wants to merge 1 commit into
pingdotgg:mainfrom
caezium:agent/usage-scan-errors

Conversation

@caezium

@caeziumcaezium commented Aug 9, 2026

Copy link
Copy Markdown
Contributor

What Changed

  • Distinguish missing transcript roots from roots that cannot be read.
  • Report nested listing/stat/file-read failures as partial coverage while retaining readable buckets.
  • Keep failed reads out of the durable cache and avoid pruning unseen cache entries after partial walks.
  • Preserve Claude's explicit-config, default nested, and legacy fallback layouts.
  • Prefer the healthiest shared source across environments (ok > partial > failed) before deduplicating buckets.

Why

The scanner currently converts I/O failures into empty results and still reports status: "ok". That silently undercounts tokens and cost while claiming complete coverage; a weaker environment can also displace a healthy copy of the same source.

Closes#5798

Verification

  • 47 focused server/web tests passed after review fixes.
  • Targeted formatting and lint passed.
  • Server and web typechecks passed; only pre-existing unrelated Effect suggestions remain.
  • Branch rebased on current upstream/main.

Checklist

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

Implemented with gpt-5.6-sol in T3 Code through the Codex harness.

Note

Report incomplete usage scans with partial, failed, and missing source statuses

  • listTranscriptFiles in usageTranscriptReader.ts now returns a structured TranscriptFileListing with rootStatus (ok | missing | failed) and a count of failedPaths instead of a plain array.
  • readSummary in UsageService.ts uses this richer result to set source status to missing, failed, or partial with descriptive messages; previously all error cases silently produced empty results.
  • readFileRecords now returns null on read failure instead of an empty array, so unreadable files are counted as failed rather than appearing as empty transcripts.
  • claimSources in usageMerge.ts now assigns shared physical transcript directories to the environment with the best coverage (ok > partial > failed > missing) rather than the first by environment ID.
  • Behavioral Change: sources that previously appeared as empty or were silently skipped now surface as partial or failed with explicit messages.

Macroscope summarized 8dd5bc2.


Note

Medium Risk
Changes usage aggregation and multi-environment deduplication logic; incorrect status or merge rules could still misreport totals, but behavior is heavily covered by new server and web tests.

Overview
Usage scanning no longer treats I/O failures as empty transcripts with status: "ok". listTranscriptFiles returns a structured listing with missing vs failed roots and a count of paths that could not be walked or stat’d; readSummary maps those to source statuses and partial when some files read but others fail, with explicit “usage may be incomplete” messages.

Failed file reads return null instead of an empty record list so they are not cached as zero usage, and scan-cache pruning only runs after a complete directory walk so partial listings do not evict unseen cached paths. Claude transcript resolution is extracted as resolveClaudeTranscriptDir: explicit homes use projects directly; default installs probe .claude/projects and only fall back to legacy projects when the nested path is absent—probe errors keep the preferred path so unreadable roots surface as failed rather than missing at the legacy location.

On the web side, mergeUsage assigns duplicate physical sources to the environment with the best coverage (ok > partial > failed > missing) instead of the first environment by id.

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

@coderabbitai

coderabbitaiBot commented Aug 9, 2026

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: 01df9a9e-59a3-4f10-8894-c20df461a3d5

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

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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 9, 2026
@caezium
caezium marked this pull request as ready for review August 9, 2026 10:25

@cursorcursorBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes using high effort and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Want fixes drafted automatically? Bugbot Autofix can create code changes for findings. A team admin can enable Autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 8dd5bc2. Configure here.

}
} catch {
// Vanished between readdir and stat.
failedPaths += 1;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Benign listing races mark scans partial

Medium Severity

Nested readdir and per-file stat failures always increment failedPaths, including ENOENT when a session file or directory disappears mid-walk. Root listing already treats ENOENT as missing, but nested races now force partial and keep the root out of walkedRoots, so normal transcript rotation can suppress cache pruning for the whole provider tree.

Additional Locations (1)
Fix in CursorFix in Web

Reviewed by Cursor Bugbot for commit 8dd5bc2. Configure here.

@macroscopeapp

Copy link
Copy Markdown
Contributor

Approvability

Verdict: Needs human review

This PR introduces new runtime behavior for usage scan status tracking (partial/failed/ok/missing) and changes merge logic to prefer complete coverage over partial - this is behavioral change in billing-adjacent code that warrants human review. Additionally, there is an unresolved Medium severity finding about listing race conditions.

You can customize Macroscope's approvability policy. 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.

[Bug]: Usage scans report complete coverage after transcript read failures

1 participant

@caezium
, '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): report incomplete usage scans - #5812

Open
caezium wants to merge 1 commit into
pingdotgg:mainfrom
caezium:agent/usage-scan-errors
Open

fix(server): report incomplete usage scans#5812
caezium wants to merge 1 commit into
pingdotgg:mainfrom
caezium:agent/usage-scan-errors

Conversation

@caezium

@caeziumcaezium commented Aug 9, 2026

Copy link
Copy Markdown
Contributor

What Changed

  • Distinguish missing transcript roots from roots that cannot be read.
  • Report nested listing/stat/file-read failures as partial coverage while retaining readable buckets.
  • Keep failed reads out of the durable cache and avoid pruning unseen cache entries after partial walks.
  • Preserve Claude's explicit-config, default nested, and legacy fallback layouts.
  • Prefer the healthiest shared source across environments (ok > partial > failed) before deduplicating buckets.

Why

The scanner currently converts I/O failures into empty results and still reports status: "ok". That silently undercounts tokens and cost while claiming complete coverage; a weaker environment can also displace a healthy copy of the same source.

Closes#5798

Verification

  • 47 focused server/web tests passed after review fixes.
  • Targeted formatting and lint passed.
  • Server and web typechecks passed; only pre-existing unrelated Effect suggestions remain.
  • Branch rebased on current upstream/main.

Checklist

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

Implemented with gpt-5.6-sol in T3 Code through the Codex harness.

Note

Report incomplete usage scans with partial, failed, and missing source statuses

  • listTranscriptFiles in usageTranscriptReader.ts now returns a structured TranscriptFileListing with rootStatus (ok | missing | failed) and a count of failedPaths instead of a plain array.
  • readSummary in UsageService.ts uses this richer result to set source status to missing, failed, or partial with descriptive messages; previously all error cases silently produced empty results.
  • readFileRecords now returns null on read failure instead of an empty array, so unreadable files are counted as failed rather than appearing as empty transcripts.
  • claimSources in usageMerge.ts now assigns shared physical transcript directories to the environment with the best coverage (ok > partial > failed > missing) rather than the first by environment ID.
  • Behavioral Change: sources that previously appeared as empty or were silently skipped now surface as partial or failed with explicit messages.

Macroscope summarized 8dd5bc2.


Note

Medium Risk
Changes usage aggregation and multi-environment deduplication logic; incorrect status or merge rules could still misreport totals, but behavior is heavily covered by new server and web tests.

Overview
Usage scanning no longer treats I/O failures as empty transcripts with status: "ok". listTranscriptFiles returns a structured listing with missing vs failed roots and a count of paths that could not be walked or stat’d; readSummary maps those to source statuses and partial when some files read but others fail, with explicit “usage may be incomplete” messages.

Failed file reads return null instead of an empty record list so they are not cached as zero usage, and scan-cache pruning only runs after a complete directory walk so partial listings do not evict unseen cached paths. Claude transcript resolution is extracted as resolveClaudeTranscriptDir: explicit homes use projects directly; default installs probe .claude/projects and only fall back to legacy projects when the nested path is absent—probe errors keep the preferred path so unreadable roots surface as failed rather than missing at the legacy location.

On the web side, mergeUsage assigns duplicate physical sources to the environment with the best coverage (ok > partial > failed > missing) instead of the first environment by id.

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

@coderabbitai

coderabbitaiBot commented Aug 9, 2026

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: 01df9a9e-59a3-4f10-8894-c20df461a3d5

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

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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 9, 2026
@caezium
caezium marked this pull request as ready for review August 9, 2026 10:25

@cursorcursorBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes using high effort and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Want fixes drafted automatically? Bugbot Autofix can create code changes for findings. A team admin can enable Autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 8dd5bc2. Configure here.

}
} catch {
// Vanished between readdir and stat.
failedPaths += 1;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Benign listing races mark scans partial

Medium Severity

Nested readdir and per-file stat failures always increment failedPaths, including ENOENT when a session file or directory disappears mid-walk. Root listing already treats ENOENT as missing, but nested races now force partial and keep the root out of walkedRoots, so normal transcript rotation can suppress cache pruning for the whole provider tree.

Additional Locations (1)
Fix in CursorFix in Web

Reviewed by Cursor Bugbot for commit 8dd5bc2. Configure here.

@macroscopeapp

Copy link
Copy Markdown
Contributor

Approvability

Verdict: Needs human review

This PR introduces new runtime behavior for usage scan status tracking (partial/failed/ok/missing) and changes merge logic to prefer complete coverage over partial - this is behavioral change in billing-adjacent code that warrants human review. Additionally, there is an unresolved Medium severity finding about listing race conditions.

You can customize Macroscope's approvability policy. 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.

[Bug]: Usage scans report complete coverage after transcript read failures

1 participant

@caezium
, '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): report incomplete usage scans - #5812

Open
caezium wants to merge 1 commit into
pingdotgg:mainfrom
caezium:agent/usage-scan-errors
Open

fix(server): report incomplete usage scans#5812
caezium wants to merge 1 commit into
pingdotgg:mainfrom
caezium:agent/usage-scan-errors

Conversation

@caezium

@caeziumcaezium commented Aug 9, 2026

Copy link
Copy Markdown
Contributor

What Changed

  • Distinguish missing transcript roots from roots that cannot be read.
  • Report nested listing/stat/file-read failures as partial coverage while retaining readable buckets.
  • Keep failed reads out of the durable cache and avoid pruning unseen cache entries after partial walks.
  • Preserve Claude's explicit-config, default nested, and legacy fallback layouts.
  • Prefer the healthiest shared source across environments (ok > partial > failed) before deduplicating buckets.

Why

The scanner currently converts I/O failures into empty results and still reports status: "ok". That silently undercounts tokens and cost while claiming complete coverage; a weaker environment can also displace a healthy copy of the same source.

Closes#5798

Verification

  • 47 focused server/web tests passed after review fixes.
  • Targeted formatting and lint passed.
  • Server and web typechecks passed; only pre-existing unrelated Effect suggestions remain.
  • Branch rebased on current upstream/main.

Checklist

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

Implemented with gpt-5.6-sol in T3 Code through the Codex harness.

Note

Report incomplete usage scans with partial, failed, and missing source statuses

  • listTranscriptFiles in usageTranscriptReader.ts now returns a structured TranscriptFileListing with rootStatus (ok | missing | failed) and a count of failedPaths instead of a plain array.
  • readSummary in UsageService.ts uses this richer result to set source status to missing, failed, or partial with descriptive messages; previously all error cases silently produced empty results.
  • readFileRecords now returns null on read failure instead of an empty array, so unreadable files are counted as failed rather than appearing as empty transcripts.
  • claimSources in usageMerge.ts now assigns shared physical transcript directories to the environment with the best coverage (ok > partial > failed > missing) rather than the first by environment ID.
  • Behavioral Change: sources that previously appeared as empty or were silently skipped now surface as partial or failed with explicit messages.

Macroscope summarized 8dd5bc2.


Note

Medium Risk
Changes usage aggregation and multi-environment deduplication logic; incorrect status or merge rules could still misreport totals, but behavior is heavily covered by new server and web tests.

Overview
Usage scanning no longer treats I/O failures as empty transcripts with status: "ok". listTranscriptFiles returns a structured listing with missing vs failed roots and a count of paths that could not be walked or stat’d; readSummary maps those to source statuses and partial when some files read but others fail, with explicit “usage may be incomplete” messages.

Failed file reads return null instead of an empty record list so they are not cached as zero usage, and scan-cache pruning only runs after a complete directory walk so partial listings do not evict unseen cached paths. Claude transcript resolution is extracted as resolveClaudeTranscriptDir: explicit homes use projects directly; default installs probe .claude/projects and only fall back to legacy projects when the nested path is absent—probe errors keep the preferred path so unreadable roots surface as failed rather than missing at the legacy location.

On the web side, mergeUsage assigns duplicate physical sources to the environment with the best coverage (ok > partial > failed > missing) instead of the first environment by id.

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

@coderabbitai

coderabbitaiBot commented Aug 9, 2026

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: 01df9a9e-59a3-4f10-8894-c20df461a3d5

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

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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 9, 2026
@caezium
caezium marked this pull request as ready for review August 9, 2026 10:25

@cursorcursorBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes using high effort and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Want fixes drafted automatically? Bugbot Autofix can create code changes for findings. A team admin can enable Autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 8dd5bc2. Configure here.

}
} catch {
// Vanished between readdir and stat.
failedPaths += 1;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Benign listing races mark scans partial

Medium Severity

Nested readdir and per-file stat failures always increment failedPaths, including ENOENT when a session file or directory disappears mid-walk. Root listing already treats ENOENT as missing, but nested races now force partial and keep the root out of walkedRoots, so normal transcript rotation can suppress cache pruning for the whole provider tree.

Additional Locations (1)
Fix in CursorFix in Web

Reviewed by Cursor Bugbot for commit 8dd5bc2. Configure here.

@macroscopeapp

Copy link
Copy Markdown
Contributor

Approvability

Verdict: Needs human review

This PR introduces new runtime behavior for usage scan status tracking (partial/failed/ok/missing) and changes merge logic to prefer complete coverage over partial - this is behavioral change in billing-adjacent code that warrants human review. Additionally, there is an unresolved Medium severity finding about listing race conditions.

You can customize Macroscope's approvability policy. 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.

[Bug]: Usage scans report complete coverage after transcript read failures

1 participant

@caezium
, '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): report incomplete usage scans - #5812

Open
caezium wants to merge 1 commit into
pingdotgg:mainfrom
caezium:agent/usage-scan-errors
Open

fix(server): report incomplete usage scans#5812
caezium wants to merge 1 commit into
pingdotgg:mainfrom
caezium:agent/usage-scan-errors

Conversation

@caezium

@caeziumcaezium commented Aug 9, 2026

Copy link
Copy Markdown
Contributor

What Changed

  • Distinguish missing transcript roots from roots that cannot be read.
  • Report nested listing/stat/file-read failures as partial coverage while retaining readable buckets.
  • Keep failed reads out of the durable cache and avoid pruning unseen cache entries after partial walks.
  • Preserve Claude's explicit-config, default nested, and legacy fallback layouts.
  • Prefer the healthiest shared source across environments (ok > partial > failed) before deduplicating buckets.

Why

The scanner currently converts I/O failures into empty results and still reports status: "ok". That silently undercounts tokens and cost while claiming complete coverage; a weaker environment can also displace a healthy copy of the same source.

Closes#5798

Verification

  • 47 focused server/web tests passed after review fixes.
  • Targeted formatting and lint passed.
  • Server and web typechecks passed; only pre-existing unrelated Effect suggestions remain.
  • Branch rebased on current upstream/main.

Checklist

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

Implemented with gpt-5.6-sol in T3 Code through the Codex harness.

Note

Report incomplete usage scans with partial, failed, and missing source statuses

  • listTranscriptFiles in usageTranscriptReader.ts now returns a structured TranscriptFileListing with rootStatus (ok | missing | failed) and a count of failedPaths instead of a plain array.
  • readSummary in UsageService.ts uses this richer result to set source status to missing, failed, or partial with descriptive messages; previously all error cases silently produced empty results.
  • readFileRecords now returns null on read failure instead of an empty array, so unreadable files are counted as failed rather than appearing as empty transcripts.
  • claimSources in usageMerge.ts now assigns shared physical transcript directories to the environment with the best coverage (ok > partial > failed > missing) rather than the first by environment ID.
  • Behavioral Change: sources that previously appeared as empty or were silently skipped now surface as partial or failed with explicit messages.

Macroscope summarized 8dd5bc2.


Note

Medium Risk
Changes usage aggregation and multi-environment deduplication logic; incorrect status or merge rules could still misreport totals, but behavior is heavily covered by new server and web tests.

Overview
Usage scanning no longer treats I/O failures as empty transcripts with status: "ok". listTranscriptFiles returns a structured listing with missing vs failed roots and a count of paths that could not be walked or stat’d; readSummary maps those to source statuses and partial when some files read but others fail, with explicit “usage may be incomplete” messages.

Failed file reads return null instead of an empty record list so they are not cached as zero usage, and scan-cache pruning only runs after a complete directory walk so partial listings do not evict unseen cached paths. Claude transcript resolution is extracted as resolveClaudeTranscriptDir: explicit homes use projects directly; default installs probe .claude/projects and only fall back to legacy projects when the nested path is absent—probe errors keep the preferred path so unreadable roots surface as failed rather than missing at the legacy location.

On the web side, mergeUsage assigns duplicate physical sources to the environment with the best coverage (ok > partial > failed > missing) instead of the first environment by id.

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

@coderabbitai

coderabbitaiBot commented Aug 9, 2026

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: 01df9a9e-59a3-4f10-8894-c20df461a3d5

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

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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 9, 2026
@caezium
caezium marked this pull request as ready for review August 9, 2026 10:25

@cursorcursorBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes using high effort and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Want fixes drafted automatically? Bugbot Autofix can create code changes for findings. A team admin can enable Autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 8dd5bc2. Configure here.

}
} catch {
// Vanished between readdir and stat.
failedPaths += 1;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Benign listing races mark scans partial

Medium Severity

Nested readdir and per-file stat failures always increment failedPaths, including ENOENT when a session file or directory disappears mid-walk. Root listing already treats ENOENT as missing, but nested races now force partial and keep the root out of walkedRoots, so normal transcript rotation can suppress cache pruning for the whole provider tree.

Additional Locations (1)
Fix in CursorFix in Web

Reviewed by Cursor Bugbot for commit 8dd5bc2. Configure here.

@macroscopeapp

Copy link
Copy Markdown
Contributor

Approvability

Verdict: Needs human review

This PR introduces new runtime behavior for usage scan status tracking (partial/failed/ok/missing) and changes merge logic to prefer complete coverage over partial - this is behavioral change in billing-adjacent code that warrants human review. Additionally, there is an unresolved Medium severity finding about listing race conditions.

You can customize Macroscope's approvability policy. 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.

[Bug]: Usage scans report complete coverage after transcript read failures

1 participant

@caezium
, '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): report incomplete usage scans - #5812

Open
caezium wants to merge 1 commit into
pingdotgg:mainfrom
caezium:agent/usage-scan-errors
Open

fix(server): report incomplete usage scans#5812
caezium wants to merge 1 commit into
pingdotgg:mainfrom
caezium:agent/usage-scan-errors

Conversation

@caezium

@caeziumcaezium commented Aug 9, 2026

Copy link
Copy Markdown
Contributor

What Changed

  • Distinguish missing transcript roots from roots that cannot be read.
  • Report nested listing/stat/file-read failures as partial coverage while retaining readable buckets.
  • Keep failed reads out of the durable cache and avoid pruning unseen cache entries after partial walks.
  • Preserve Claude's explicit-config, default nested, and legacy fallback layouts.
  • Prefer the healthiest shared source across environments (ok > partial > failed) before deduplicating buckets.

Why

The scanner currently converts I/O failures into empty results and still reports status: "ok". That silently undercounts tokens and cost while claiming complete coverage; a weaker environment can also displace a healthy copy of the same source.

Closes#5798

Verification

  • 47 focused server/web tests passed after review fixes.
  • Targeted formatting and lint passed.
  • Server and web typechecks passed; only pre-existing unrelated Effect suggestions remain.
  • Branch rebased on current upstream/main.

Checklist

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

Implemented with gpt-5.6-sol in T3 Code through the Codex harness.

Note

Report incomplete usage scans with partial, failed, and missing source statuses

  • listTranscriptFiles in usageTranscriptReader.ts now returns a structured TranscriptFileListing with rootStatus (ok | missing | failed) and a count of failedPaths instead of a plain array.
  • readSummary in UsageService.ts uses this richer result to set source status to missing, failed, or partial with descriptive messages; previously all error cases silently produced empty results.
  • readFileRecords now returns null on read failure instead of an empty array, so unreadable files are counted as failed rather than appearing as empty transcripts.
  • claimSources in usageMerge.ts now assigns shared physical transcript directories to the environment with the best coverage (ok > partial > failed > missing) rather than the first by environment ID.
  • Behavioral Change: sources that previously appeared as empty or were silently skipped now surface as partial or failed with explicit messages.

Macroscope summarized 8dd5bc2.


Note

Medium Risk
Changes usage aggregation and multi-environment deduplication logic; incorrect status or merge rules could still misreport totals, but behavior is heavily covered by new server and web tests.

Overview
Usage scanning no longer treats I/O failures as empty transcripts with status: "ok". listTranscriptFiles returns a structured listing with missing vs failed roots and a count of paths that could not be walked or stat’d; readSummary maps those to source statuses and partial when some files read but others fail, with explicit “usage may be incomplete” messages.

Failed file reads return null instead of an empty record list so they are not cached as zero usage, and scan-cache pruning only runs after a complete directory walk so partial listings do not evict unseen cached paths. Claude transcript resolution is extracted as resolveClaudeTranscriptDir: explicit homes use projects directly; default installs probe .claude/projects and only fall back to legacy projects when the nested path is absent—probe errors keep the preferred path so unreadable roots surface as failed rather than missing at the legacy location.

On the web side, mergeUsage assigns duplicate physical sources to the environment with the best coverage (ok > partial > failed > missing) instead of the first environment by id.

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

@coderabbitai

coderabbitaiBot commented Aug 9, 2026

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: 01df9a9e-59a3-4f10-8894-c20df461a3d5

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

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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 9, 2026
@caezium
caezium marked this pull request as ready for review August 9, 2026 10:25

@cursorcursorBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes using high effort and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Want fixes drafted automatically? Bugbot Autofix can create code changes for findings. A team admin can enable Autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 8dd5bc2. Configure here.

}
} catch {
// Vanished between readdir and stat.
failedPaths += 1;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Benign listing races mark scans partial

Medium Severity

Nested readdir and per-file stat failures always increment failedPaths, including ENOENT when a session file or directory disappears mid-walk. Root listing already treats ENOENT as missing, but nested races now force partial and keep the root out of walkedRoots, so normal transcript rotation can suppress cache pruning for the whole provider tree.

Additional Locations (1)
Fix in CursorFix in Web

Reviewed by Cursor Bugbot for commit 8dd5bc2. Configure here.

@macroscopeapp

Copy link
Copy Markdown
Contributor

Approvability

Verdict: Needs human review

This PR introduces new runtime behavior for usage scan status tracking (partial/failed/ok/missing) and changes merge logic to prefer complete coverage over partial - this is behavioral change in billing-adjacent code that warrants human review. Additionally, there is an unresolved Medium severity finding about listing race conditions.

You can customize Macroscope's approvability policy. 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.

[Bug]: Usage scans report complete coverage after transcript read failures

1 participant

@caezium
, '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): report incomplete usage scans - #5812

Open
caezium wants to merge 1 commit into
pingdotgg:mainfrom
caezium:agent/usage-scan-errors
Open

fix(server): report incomplete usage scans#5812
caezium wants to merge 1 commit into
pingdotgg:mainfrom
caezium:agent/usage-scan-errors

Conversation

@caezium

@caeziumcaezium commented Aug 9, 2026

Copy link
Copy Markdown
Contributor

What Changed

  • Distinguish missing transcript roots from roots that cannot be read.
  • Report nested listing/stat/file-read failures as partial coverage while retaining readable buckets.
  • Keep failed reads out of the durable cache and avoid pruning unseen cache entries after partial walks.
  • Preserve Claude's explicit-config, default nested, and legacy fallback layouts.
  • Prefer the healthiest shared source across environments (ok > partial > failed) before deduplicating buckets.

Why

The scanner currently converts I/O failures into empty results and still reports status: "ok". That silently undercounts tokens and cost while claiming complete coverage; a weaker environment can also displace a healthy copy of the same source.

Closes#5798

Verification

  • 47 focused server/web tests passed after review fixes.
  • Targeted formatting and lint passed.
  • Server and web typechecks passed; only pre-existing unrelated Effect suggestions remain.
  • Branch rebased on current upstream/main.

Checklist

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

Implemented with gpt-5.6-sol in T3 Code through the Codex harness.

Note

Report incomplete usage scans with partial, failed, and missing source statuses

  • listTranscriptFiles in usageTranscriptReader.ts now returns a structured TranscriptFileListing with rootStatus (ok | missing | failed) and a count of failedPaths instead of a plain array.
  • readSummary in UsageService.ts uses this richer result to set source status to missing, failed, or partial with descriptive messages; previously all error cases silently produced empty results.
  • readFileRecords now returns null on read failure instead of an empty array, so unreadable files are counted as failed rather than appearing as empty transcripts.
  • claimSources in usageMerge.ts now assigns shared physical transcript directories to the environment with the best coverage (ok > partial > failed > missing) rather than the first by environment ID.
  • Behavioral Change: sources that previously appeared as empty or were silently skipped now surface as partial or failed with explicit messages.

Macroscope summarized 8dd5bc2.


Note

Medium Risk
Changes usage aggregation and multi-environment deduplication logic; incorrect status or merge rules could still misreport totals, but behavior is heavily covered by new server and web tests.

Overview
Usage scanning no longer treats I/O failures as empty transcripts with status: "ok". listTranscriptFiles returns a structured listing with missing vs failed roots and a count of paths that could not be walked or stat’d; readSummary maps those to source statuses and partial when some files read but others fail, with explicit “usage may be incomplete” messages.

Failed file reads return null instead of an empty record list so they are not cached as zero usage, and scan-cache pruning only runs after a complete directory walk so partial listings do not evict unseen cached paths. Claude transcript resolution is extracted as resolveClaudeTranscriptDir: explicit homes use projects directly; default installs probe .claude/projects and only fall back to legacy projects when the nested path is absent—probe errors keep the preferred path so unreadable roots surface as failed rather than missing at the legacy location.

On the web side, mergeUsage assigns duplicate physical sources to the environment with the best coverage (ok > partial > failed > missing) instead of the first environment by id.

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

@coderabbitai

coderabbitaiBot commented Aug 9, 2026

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: 01df9a9e-59a3-4f10-8894-c20df461a3d5

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

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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 9, 2026
@caezium
caezium marked this pull request as ready for review August 9, 2026 10:25

@cursorcursorBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes using high effort and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Want fixes drafted automatically? Bugbot Autofix can create code changes for findings. A team admin can enable Autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 8dd5bc2. Configure here.

}
} catch {
// Vanished between readdir and stat.
failedPaths += 1;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Benign listing races mark scans partial

Medium Severity

Nested readdir and per-file stat failures always increment failedPaths, including ENOENT when a session file or directory disappears mid-walk. Root listing already treats ENOENT as missing, but nested races now force partial and keep the root out of walkedRoots, so normal transcript rotation can suppress cache pruning for the whole provider tree.

Additional Locations (1)
Fix in CursorFix in Web

Reviewed by Cursor Bugbot for commit 8dd5bc2. Configure here.

@macroscopeapp

Copy link
Copy Markdown
Contributor

Approvability

Verdict: Needs human review

This PR introduces new runtime behavior for usage scan status tracking (partial/failed/ok/missing) and changes merge logic to prefer complete coverage over partial - this is behavioral change in billing-adjacent code that warrants human review. Additionally, there is an unresolved Medium severity finding about listing race conditions.

You can customize Macroscope's approvability policy. 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.

[Bug]: Usage scans report complete coverage after transcript read failures

1 participant

@caezium
, '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): report incomplete usage scans - #5812

Open
caezium wants to merge 1 commit into
pingdotgg:mainfrom
caezium:agent/usage-scan-errors
Open

fix(server): report incomplete usage scans#5812
caezium wants to merge 1 commit into
pingdotgg:mainfrom
caezium:agent/usage-scan-errors

Conversation

@caezium

@caeziumcaezium commented Aug 9, 2026

Copy link
Copy Markdown
Contributor

What Changed

  • Distinguish missing transcript roots from roots that cannot be read.
  • Report nested listing/stat/file-read failures as partial coverage while retaining readable buckets.
  • Keep failed reads out of the durable cache and avoid pruning unseen cache entries after partial walks.
  • Preserve Claude's explicit-config, default nested, and legacy fallback layouts.
  • Prefer the healthiest shared source across environments (ok > partial > failed) before deduplicating buckets.

Why

The scanner currently converts I/O failures into empty results and still reports status: "ok". That silently undercounts tokens and cost while claiming complete coverage; a weaker environment can also displace a healthy copy of the same source.

Closes#5798

Verification

  • 47 focused server/web tests passed after review fixes.
  • Targeted formatting and lint passed.
  • Server and web typechecks passed; only pre-existing unrelated Effect suggestions remain.
  • Branch rebased on current upstream/main.

Checklist

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

Implemented with gpt-5.6-sol in T3 Code through the Codex harness.

Note

Report incomplete usage scans with partial, failed, and missing source statuses

  • listTranscriptFiles in usageTranscriptReader.ts now returns a structured TranscriptFileListing with rootStatus (ok | missing | failed) and a count of failedPaths instead of a plain array.
  • readSummary in UsageService.ts uses this richer result to set source status to missing, failed, or partial with descriptive messages; previously all error cases silently produced empty results.
  • readFileRecords now returns null on read failure instead of an empty array, so unreadable files are counted as failed rather than appearing as empty transcripts.
  • claimSources in usageMerge.ts now assigns shared physical transcript directories to the environment with the best coverage (ok > partial > failed > missing) rather than the first by environment ID.
  • Behavioral Change: sources that previously appeared as empty or were silently skipped now surface as partial or failed with explicit messages.

Macroscope summarized 8dd5bc2.


Note

Medium Risk
Changes usage aggregation and multi-environment deduplication logic; incorrect status or merge rules could still misreport totals, but behavior is heavily covered by new server and web tests.

Overview
Usage scanning no longer treats I/O failures as empty transcripts with status: "ok". listTranscriptFiles returns a structured listing with missing vs failed roots and a count of paths that could not be walked or stat’d; readSummary maps those to source statuses and partial when some files read but others fail, with explicit “usage may be incomplete” messages.

Failed file reads return null instead of an empty record list so they are not cached as zero usage, and scan-cache pruning only runs after a complete directory walk so partial listings do not evict unseen cached paths. Claude transcript resolution is extracted as resolveClaudeTranscriptDir: explicit homes use projects directly; default installs probe .claude/projects and only fall back to legacy projects when the nested path is absent—probe errors keep the preferred path so unreadable roots surface as failed rather than missing at the legacy location.

On the web side, mergeUsage assigns duplicate physical sources to the environment with the best coverage (ok > partial > failed > missing) instead of the first environment by id.

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

@coderabbitai

coderabbitaiBot commented Aug 9, 2026

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: 01df9a9e-59a3-4f10-8894-c20df461a3d5

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

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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 9, 2026
@caezium
caezium marked this pull request as ready for review August 9, 2026 10:25

@cursorcursorBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes using high effort and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Want fixes drafted automatically? Bugbot Autofix can create code changes for findings. A team admin can enable Autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 8dd5bc2. Configure here.

}
} catch {
// Vanished between readdir and stat.
failedPaths += 1;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Benign listing races mark scans partial

Medium Severity

Nested readdir and per-file stat failures always increment failedPaths, including ENOENT when a session file or directory disappears mid-walk. Root listing already treats ENOENT as missing, but nested races now force partial and keep the root out of walkedRoots, so normal transcript rotation can suppress cache pruning for the whole provider tree.

Additional Locations (1)
Fix in CursorFix in Web

Reviewed by Cursor Bugbot for commit 8dd5bc2. Configure here.

@macroscopeapp

Copy link
Copy Markdown
Contributor

Approvability

Verdict: Needs human review

This PR introduces new runtime behavior for usage scan status tracking (partial/failed/ok/missing) and changes merge logic to prefer complete coverage over partial - this is behavioral change in billing-adjacent code that warrants human review. Additionally, there is an unresolved Medium severity finding about listing race conditions.

You can customize Macroscope's approvability policy. 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.

[Bug]: Usage scans report complete coverage after transcript read failures

1 participant

@caezium
, '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): report incomplete usage scans - #5812

Open
caezium wants to merge 1 commit into
pingdotgg:mainfrom
caezium:agent/usage-scan-errors
Open

fix(server): report incomplete usage scans#5812
caezium wants to merge 1 commit into
pingdotgg:mainfrom
caezium:agent/usage-scan-errors

Conversation

@caezium

@caeziumcaezium commented Aug 9, 2026

Copy link
Copy Markdown
Contributor

What Changed

  • Distinguish missing transcript roots from roots that cannot be read.
  • Report nested listing/stat/file-read failures as partial coverage while retaining readable buckets.
  • Keep failed reads out of the durable cache and avoid pruning unseen cache entries after partial walks.
  • Preserve Claude's explicit-config, default nested, and legacy fallback layouts.
  • Prefer the healthiest shared source across environments (ok > partial > failed) before deduplicating buckets.

Why

The scanner currently converts I/O failures into empty results and still reports status: "ok". That silently undercounts tokens and cost while claiming complete coverage; a weaker environment can also displace a healthy copy of the same source.

Closes#5798

Verification

  • 47 focused server/web tests passed after review fixes.
  • Targeted formatting and lint passed.
  • Server and web typechecks passed; only pre-existing unrelated Effect suggestions remain.
  • Branch rebased on current upstream/main.

Checklist

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

Implemented with gpt-5.6-sol in T3 Code through the Codex harness.

Note

Report incomplete usage scans with partial, failed, and missing source statuses

  • listTranscriptFiles in usageTranscriptReader.ts now returns a structured TranscriptFileListing with rootStatus (ok | missing | failed) and a count of failedPaths instead of a plain array.
  • readSummary in UsageService.ts uses this richer result to set source status to missing, failed, or partial with descriptive messages; previously all error cases silently produced empty results.
  • readFileRecords now returns null on read failure instead of an empty array, so unreadable files are counted as failed rather than appearing as empty transcripts.
  • claimSources in usageMerge.ts now assigns shared physical transcript directories to the environment with the best coverage (ok > partial > failed > missing) rather than the first by environment ID.
  • Behavioral Change: sources that previously appeared as empty or were silently skipped now surface as partial or failed with explicit messages.

Macroscope summarized 8dd5bc2.


Note

Medium Risk
Changes usage aggregation and multi-environment deduplication logic; incorrect status or merge rules could still misreport totals, but behavior is heavily covered by new server and web tests.

Overview
Usage scanning no longer treats I/O failures as empty transcripts with status: "ok". listTranscriptFiles returns a structured listing with missing vs failed roots and a count of paths that could not be walked or stat’d; readSummary maps those to source statuses and partial when some files read but others fail, with explicit “usage may be incomplete” messages.

Failed file reads return null instead of an empty record list so they are not cached as zero usage, and scan-cache pruning only runs after a complete directory walk so partial listings do not evict unseen cached paths. Claude transcript resolution is extracted as resolveClaudeTranscriptDir: explicit homes use projects directly; default installs probe .claude/projects and only fall back to legacy projects when the nested path is absent—probe errors keep the preferred path so unreadable roots surface as failed rather than missing at the legacy location.

On the web side, mergeUsage assigns duplicate physical sources to the environment with the best coverage (ok > partial > failed > missing) instead of the first environment by id.

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

@coderabbitai

coderabbitaiBot commented Aug 9, 2026

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: 01df9a9e-59a3-4f10-8894-c20df461a3d5

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

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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 9, 2026
@caezium
caezium marked this pull request as ready for review August 9, 2026 10:25

@cursorcursorBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes using high effort and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Want fixes drafted automatically? Bugbot Autofix can create code changes for findings. A team admin can enable Autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 8dd5bc2. Configure here.

}
} catch {
// Vanished between readdir and stat.
failedPaths += 1;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Benign listing races mark scans partial

Medium Severity

Nested readdir and per-file stat failures always increment failedPaths, including ENOENT when a session file or directory disappears mid-walk. Root listing already treats ENOENT as missing, but nested races now force partial and keep the root out of walkedRoots, so normal transcript rotation can suppress cache pruning for the whole provider tree.

Additional Locations (1)
Fix in CursorFix in Web

Reviewed by Cursor Bugbot for commit 8dd5bc2. Configure here.

@macroscopeapp

Copy link
Copy Markdown
Contributor

Approvability

Verdict: Needs human review

This PR introduces new runtime behavior for usage scan status tracking (partial/failed/ok/missing) and changes merge logic to prefer complete coverage over partial - this is behavioral change in billing-adjacent code that warrants human review. Additionally, there is an unresolved Medium severity finding about listing race conditions.

You can customize Macroscope's approvability policy. 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.

[Bug]: Usage scans report complete coverage after transcript read failures

1 participant

@caezium