Skip to content

feat(trackers,browser): ProcessTracker (heap, main-thread lag, idle, user idle, uptime) (TR5) - #83

Merged
krassx merged 9 commits into
mainfrom
feat/process-tracker
Oct 20, 2026
Merged

krassx merged 9 commits into
mainfrom
feat/process-tracker

Conversation

@krassx

@krassx krassx commented Oct 20, 2026 •

Copy link
Copy Markdown
Contributor

Umbrella and hand-off: #53.

What

  • packages/trackers/src/browser/process-tracker.ts (+test): domain tracker with stages heap, mainThreadLag (timer drift, same algorithm as feat(browser,webview): screen_size and main_thread_lag_ms traces (ST4c) #75), idle (requestIdleCallback once per sampling tick, visible pages only), userIdle (Idle Detection API ONLY when the Permissions API already reports granted; the SDK never prompts) and uptime.
  • packages/browser/src/trace-producers.ts rewired onto it; new traces main_thread_idle_ms and user_idle_state. The old memory-tracker.ts and main-thread-lag-tracker.ts are removed (no remaining references).
  • Per-launch seams reach the shared singleton through registry rebind; browserTraceEnvFrom now caches bound closures per (window, fn) so a no-op relaunch does not restart armed stages.
  • Changeset and docs/PROGRESS.md updated.

Verification

  • Test-first; mutator loop run on every changed entity (mutations rolled back from saved copies).
  • Four read-only Opus review rounds; all real findings fixed test-first. The last round found no implementation issues, only three test gaps, now closed.
  • Lint, typecheck (102/102), cycles pass; coverage 100% line/function/statement, branch 99.15% (trackers) / 98.78% (browser) / 99.71% (webview).
  • Note: 3 @bugsee/node-utils chmod tests fail only when run as root (sandbox); unrelated to this change.

Open owner decision

user_idle_state is a held value: a revoke cannot retract an already-recorded value (the seed is age-bounded and gated on a live, confirmed detector, but a producer that already published keeps re-recording). Same family as the pending storage_estimate decision recorded in docs/PROGRESS.md.

🤖 Generated with Claude Code

https://claude.ai/code/session_01A1UFAUHj4nozT7kLwBgMrC

…i:local / review yet

Work stopped mid-way for a session hand-off; see the draft PR.

🤖 Generated with [Claude Code](https://claude.com/claude-code)
@coderabbitai

coderabbitai Bot commented Oct 20, 2026 •

Copy link
Copy Markdown

Review in Change Stack →

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration
  • Configuration used: Organization UI
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: 127e7c60-1e91-44d9-9b71-09136d6bf2a9
📥 Commits

Reviewing files that changed from the base of the PR and between 7d84773 and c667c65.

📒 Files selected for processing: 14
  • .changeset/browser-process-tracker.md
  • docs/PROGRESS.md
  • packages/browser/src/index.ts
  • packages/browser/src/launch-options.invalid.test.ts
  • packages/browser/src/launch.test.ts
  • packages/browser/src/launch.ts
  • packages/browser/src/trace-producers.process.test.ts
  • packages/browser/src/trace-producers.sensors.test.ts
  • packages/browser/src/trace-producers.ts
  • packages/bugsee/src/option-directions.test.ts
  • packages/bugsee/src/relaunch-options.test.ts
  • packages/trackers/README.md
  • packages/webview/src/launch.test.ts
  • packages/webview/src/launch.ts
💤 Files with no reviewable changes: 1
  • packages/browser/src/trace-producers.sensors.test.ts
🚧 Files skipped from review as they are similar to previous changes: 2
  • .changeset/browser-process-tracker.md
  • packages/trackers/README.md

Included review availability: This review used your included allowance. Your plan provides up to 10 included reviews per hour; 1 remain after this review.

📝 Summary

Summary by CodeRabbit

  • New Features

    • Added browser and WebView telemetry for visible-page main-thread idle time.
    • Added optional user idle-state tracking when Idle Detection permission is already granted, without prompting.
    • Added coordinated process metrics for heap usage, main-thread lag, idle time, user idle state, and uptime.
    • Metrics activate only when needed and remain disabled by default unless explicitly enabled.
  • Documentation

    • Updated tracker documentation to describe process metrics and permission requirements.

The browser tracker package adds a per-window ProcessTracker with listener-activated heap, uptime, main-thread lag, idle, and user-idle stages. Browser trace producers share this tracker and emit heap, main-thread lag, main-thread idle, and user-idle traces. User-idle tracking uses Idle Detection only when permission is already granted. The separate memory and main-thread lag trackers are removed.

Sequence Diagram(s)

sequenceDiagram
  participant Window
  participant browserTraceEnvFrom
  participant createBrowserSystemTraceProducers
  participant processTrackerFor
  participant ProcessTracker
  participant UserIdleProducer
  Window->>browserTraceEnvFrom: provides window and idle-detection APIs
  browserTraceEnvFrom->>createBrowserSystemTraceProducers: supplies browser environment
  createBrowserSystemTraceProducers->>processTrackerFor: requests tracker for environment
  processTrackerFor->>ProcessTracker: creates or rebinds per-window tracker
  createBrowserSystemTraceProducers->>UserIdleProducer: supplies shared tracker
  UserIdleProducer->>ProcessTracker: subscribes to user-idle state
Loading

Suggested reviewers: bugseeai

Priority: ➖ Normal

Change: Feature

Merge Risk: ⚪ Minimal · up to c667c

The reviewed launch and relaunch paths preserve their stated idle-capture behavior. No identified issue prevents merging after normal checks.

Security architecture risk: 🟡 Moderate · up to c667c

User-idle capture is disabled by default and requires an existing browser permission grant. However, after permission is revoked, the last recorded idle or locked state continues to be recorded with fresh timestamps until tracing stops. This limits the effectiveness of revocation even though the detector stops collecting new observations.

Retained concerns

  • Medium · security · observed: Permission revocation stops the detector and clears the tracker's retained state, but does not invalidate the user_idle_state already held by the system-traces provider. The provider therefore continues recording the old behavioral value with fresh timestamps until it stops. The new permission-sensitive trace inherits an existing retention mechanism without a corresponding authorization-invalidation transition.
Security review details

Security Blast Radius

  • inferred — The demonstrated exposure is behavioral state associated with an opted-in Browser or WebView capture session that has obtained a valid detector reading. It reaches the existing Browser report-capture path or WebView native-host stream. Downstream native and server retention and access were not verified, so end-to-end exposure is not established.

Security Findings and Attack Paths

  • observed — The problematic transition is grant, initial sample, permission revocation, detector abort, then continued refresh of the previously held sample. This is continued recording of old behavioral data, not demonstrated collection of fresh activity after revocation. Clearing the tracker's snapshot protects new seeds but does not clear the provider's independent held entry.

Trust Boundaries and Controls

  • observed — The strongest controls are default-off launch configuration, exact-true producer construction, and an already-granted browser permission. Pending permission-query callbacks check run lifetime; detector-start completion checks both lifetime and detector identity. Revocation removes the detector listener and aborts its controller, limiting the concern to downstream retention rather than an authorization bypass for new observations.

Resilience and Maintainability Implications

  • observed — Shared detector ownership follows listener count, so removing the final listener tears down its stage. Rebind tears down armed stages using their captured old resources before replacing seams. Teardown invalidates pending asynchronous work and removes permission listeners. These mechanisms contain stale detector activity, but do not invalidate downstream held trace state.

Hardening Proposals

  • proposed — Define a revocation transition that invalidates user_idle_state in both producer deduplication and provider-held state. Stop generating fresh records for the withdrawn value without implying deletion of legitimate historical records. On regrant, require a newly confirmed detector reading before resuming the trace.
Pre-merge checks: Passed 4 · Failed 1

Failed Failed checks: 1 warning

Check name Status Explanation Resolution
Docstring Coverage Warning Docstring coverage is 37.50% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 24 functions across 19 files. (3 skipped:… Write docstrings for the functions missing them to satisfy the coverage threshold.
Passed Passed checks: 4 passed
Check name Status Explanation
Title check Passed The title clearly identifies the main change: adding ProcessTracker support for heap, main-thread lag, idle, user idle, and uptime across the trackers and browser packages.
Description check Passed The description directly explains the ProcessTracker implementation, trace changes, removed trackers, permission behavior, testing, and the open owner decision.
Linked Issues check Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check Passed Check skipped because no linked issues were found for this pull request.
Full details: Docstring Coverage

Explanation

Docstring coverage is 37.50% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 24 functions across 19 files. (3 skipped: 3 unsupported.)

Resolution

Write docstrings for the functions missing them to satisfy the coverage threshold.

  • Fix all pre-merge checks with AI
✨ Finishing Touches: 💡 1
📝 Generate docstrings: 💡
  • Commit to this branch
  • Create a new PR
🧪 Generate unit tests (beta)
  • Commit to this branch
  • Create a new PR
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

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

…user idle, uptime domain tracker) (TR5)

Mutator loop run on the new tracker; lint fixes and a stale-idle-callback test.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01A1UFAUHj4nozT7kLwBgMrC
…lock read once, no NaN lag, teardown finally, test strength

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01A1UFAUHj4nozT7kLwBgMrC
…ssion, clock retry, single hostile-clock report, stable env closures, test gaps

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01A1UFAUHj4nozT7kLwBgMrC
…no phantom run on sync query failure, reset drops listeners, dead field removed

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01A1UFAUHj4nozT7kLwBgMrC
…uery receiver, lag with custom interval

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01A1UFAUHj4nozT7kLwBgMrC
@bugseeai bugseeai changed the title WIP feat(trackers,browser): ProcessTracker (heap, main-thread lag, idle, user idle, uptime) (TR5) feat(trackers,browser): ProcessTracker (heap, main-thread lag, idle, user idle, uptime) (TR5) Oct 20, 2026
@bugseeai
bugseeai marked this pull request as ready for review October 20, 2026 10:52
@claude

claude Bot commented Oct 20, 2026 •

Copy link
Copy Markdown

Claude finished @bugseeai's task in 1m 1s —— View job


Review of PR #83: ProcessTracker (TR5)

Scope reviewed: process-tracker.ts in full, the emitter hook change, the registry/rebind path, the trace-producers.ts rewiring (including browserTraceEnvFrom / boundTo), the changeset and the PROGRESS entry. I read the code and traced the lifecycle paths by hand. I did not run tests, typecheck or lint. node_modules isn't installed in this sandbox and the test attempt exited with an error, so the PR's coverage and gate numbers are unverified by me.

Findings

No new P0–P2 issues. I tried to break these paths and found no failing scenario:

  • Stage lifecycle: teardown, rebind while armed, and re-entrant listener changes mid-dispatch (#sync is idempotent and #stop / #start are guarded).
  • Async continuations: the permission query, IdleDetector.start(), change events and idle callbacks are all guarded by the run's alive flag or by pending !== mine.
  • Lag baseline: NaN and non-finite baselines are omitted instead of sent. Hidden→visible takes a fresh baseline.
  • rebind merge: rebind can't drop a seam, and stable bound closures keep a no-op relaunch from restarting stages.
  • Idle-detection permission: the permission is only ever queried, never requested.

P3 (low), not blocking: seed path in UserIdleProducer.onStart

  • Location: packages/browser/src/trace-producers.ts, UserIdleProducer.onStart, with ProcessTracker.currentUserIdleFresh in process-tracker.ts.
  • Problem: onStart calls tracker.on('userIdle', …) first. That call arms the stage via onListenersChanged, so #userIdleRun is already set (and published is still false) when currentUserIdleFresh() runs. The armed branch returns undefined.
  • Impact: the age-bounded "no armed stage" branch (USER_IDLE_FRESH_THRESHOLDS) is reachable from this producer only when the stage is inert, for example a synchronous query() failure with state retained from an earlier run. The held trace is still seeded correctly, by the detector's own publish after start() resolves. The seed is just not the mechanism for a first subscriber.
  • Fix (optional): read the seed before subscribing, or note in the docs that the seed only matters when another consumer has already armed the stage. Behaviour is safe either way.

Known limit (already disclosed, no new action)

user_idle_state is a held trace. After a permission revoke, a producer that has already published keeps re-recording the last value until it stops, because there is no retract signal. The PR body and PROGRESS already flag this as an open owner decision, and the same family covers storage_estimate. I agree it should be settled before this ships, since it can record a user-idle state for a user who has withdrawn consent. The PR does gate the seed on a live, re-confirmed detector.

Summary

  1. Overall risk: Low
  2. Merge recommendation: Approve once CI is green, with the owner decision on retracting held user_idle_state recorded before release. I can't confirm CI or coverage myself.
  3. Most important items:
    • Resolve the held-trace-after-revoke decision.
    • Optionally address the P3 seed-ordering note above.
    • Confirm CI on the latest push (origin/main...HEAD is 6 commits, including a wip commit — check whether it should be squashed).
  4. Positives:
    • Absence is treated as absence: unusable values are omitted, never sent as 0.
    • One idle probe in flight at most.
    • The SDK never prompts for idle detection.
    • Every async continuation is guarded by its run.
    • Hostile host getters are read through hostRead and reported once.
    • The old per-signal trackers are removed with no dangling references.
    • Tests are extensive, and the PR documents its review rounds.

bugseeai pushed a commit that referenced this pull request Oct 20, 2026
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01A1UFAUHj4nozT7kLwBgMrC
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01A1UFAUHj4nozT7kLwBgMrC
@claude

claude Bot commented Oct 20, 2026 •

Copy link
Copy Markdown

Claude finished @bugseeai's task in 1m 9s —— View job


Deep review of PR #83: ProcessTracker (TR5), re-run on the latest push

I read process-tracker.ts in full, plus the trace-producers.ts rewiring, emitter.ts, and the changeset. I traced the lifecycle paths by hand. I did not run tests, typecheck, lint or coverage, so the PR's gate numbers are unverified by me.

  • Read the PR context and my earlier review, so findings aren't repeated
  • Review the tracker, the producers and the env wiring
  • Post the findings and summary

Earlier findings

  • P3 seed ordering in UserIdleProducer.onStart: the code is unchanged, so it still applies. It is not blocking and I'm not repeating it.
  • Held user_idle_state after a revoke: still open, and still the owner decision the PR already flags.

New findings (inline)

  1. P2, consent scope of user_idle_state (process-tracker.ts:664). The SDK starts its own IdleDetector whenever the host page already holds the idle-detection grant, and the trace is on by default. The user granted that permission to the site for its own feature, not to have idle/locked state uploaded to Bugsee. This is separate from the revoke/retract question. I'd settle it with an opt-in or a privacy-doc note before release.
  2. P3, stale changeset claim (.changeset/browser-process-tracker.md:7). The changeset says MultiKeyEmitterBase gains listenerCount / onListenersChanged, but emitter.ts is not in this PR's diff. Both hooks are already on main, and ui-tracker.ts uses them.

What I tried to break and could not

  • Async continuations: permission query, IdleDetector.start(), change events and the idle callback are guarded by the run's alive flag or by pending !== mine.
  • Revoke then re-grant: end() clears run.detector, so begin() can restart. #userIdle is dropped on revoke, so a re-grant cannot serve the old state before a new publish.
  • rebind: it detaches stages with the seams they were armed with before swapping. Stable boundTo closures and the singleton applicationTrackerFor lifecycle make a no-op relaunch a true no-op.
  • Lag: unreadable or non-finite clock reads advance the baseline rather than producing NaN or fake lag, and hidden→visible takes a fresh baseline.
  • Idle: at most one probe is in flight, and teardown cancels it. A synchronous request() throw rolls pending back.
  • Memory: boundTo uses nested WeakMaps, so I see no leak.

Summary

  1. Overall risk: Low
  2. Merge recommendation: Approve once CI is green and the consent decision is recorded: the revoke/retract behaviour plus whether user_idle_state should be opt-in. Nothing here is a correctness blocker.
  3. Most important items:
    • Decide opt-in versus default-on for user_idle_state, and settle held-trace retraction.
    • Fix the stale changeset sentence.
    • The history still includes the wip(trackers) commit 13b0f142 and a merge commit. Squash on merge if you want a clean main.
  4. Positives:
    • Absence is never sent as 0.
    • The SDK never prompts for idle detection.
    • Every async continuation is run-guarded.
    • Hostile host getters are read through hostRead and reported once.
    • The old per-signal trackers are removed with no dangling references.
    • Test depth is high.
      · branch feat/process-tracker

};

try {
void Promise.resolve(query.call(permissions, { name: 'idle-detection' })).then(

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 — Location: #startUserIdle / UserIdleProducer (trace-producers.ts), wired on by default in createBrowserSystemTraceProducers.

Problem: The idle-detection permission check only shows that the host page was granted the permission. It does not show consent to record the user's idle or screen-locked state and upload it to Bugsee. The SDK never prompts, but it reuses a grant the user gave to the site for its own feature.

Impact: user_idle_state (active/idle/locked) is behavioural data. It lands in every bug report for any app that already uses Idle Detection, with no SDK-level switch. This sits in the same consent family as the held-trace-after-revoke decision, but it is a separate question: this one covers collection on a grant the SDK never asked for, while the other covers retraction after a revoke.

Scenario: A customer app asks for idle-detection to dim its own UI. After the user accepts, the SDK starts a second IdleDetector on the same grant. Locked/idle transitions are then written to traces.system and uploaded.

Fix: Record the owner decision before release. Consider an explicit opt-in (e.g. a launch option defaulting to off) for the userIdle producer, or document the behaviour prominently in the privacy docs. Pair it with the pending retract-on-revoke decision.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Addressed in c667c65. Decision (the maintainer's, pending owner confirmation): user_idle_state is now OPT-IN. A page's own idle-detection grant is not consent to record and upload idle/locked state.

  • New launch option captureUserIdle (browser and WebView launch; default false; only boolean true enables it, any other value stays off and is reported). createBrowserSystemTraceProducers builds UserIdleProducer only for captureUserIdle === true, so without the opt-in the permission is not even queried.
  • When on, the tracker still starts a detector only if the permission already reports granted; the SDK never prompts.
  • Tests: off-by-default, on+granted, on+prompt/denied, factory-level and launch-level (browser and WebView), invalid-value tests, option classification tests. Mutations on the gate, the launch pass-through and the default were each caught.
  • README, docs/PROGRESS.md and the changeset updated. This applies only to user_idle_state; private-mode detection is a separate decision. The retract-on-revoke question for held traces is unchanged and still pending.

Generated by Claude Code

Comment thread .changeset/browser-process-tracker.md Outdated
'@bugsee/trackers': minor
---

Browser and WebView system traces gain `main_thread_idle_ms` (the browser's granted idle period, 0-50 ms, from one `requestIdleCallback` probe per sampling tick on visible pages; omitted where `requestIdleCallback` is absent, and for a tick in which no idle period was granted) and `user_idle_state` (`active`, `idle` or `locked`, from the Idle Detection API). `user_idle_state` is produced only when the page's `idle-detection` permission is already granted: the SDK never prompts for it. The heap (`ram_js_heap_*`) and `main_thread_lag_ms` traces are unchanged but now come from one per-window `ProcessTracker` (new in `@bugsee/trackers/browser`), whose stages (`heap`, `mainThreadLag`, `idle`, `userIdle`, `uptime`) run only while they have listeners. `MultiKeyEmitterBase` gains the protected `listenerCount` / `onListenersChanged` hooks.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P3 — Problem: The last sentence says MultiKeyEmitterBase "gains the protected listenerCount / onListenersChanged hooks". This PR does not touch packages/trackers/src/emitter.ts. Both hooks are already on main (emitter.ts:90, :98) and ui-tracker.ts already uses them.

Impact: The changelog for @bugsee/trackers would attribute an API addition to the wrong release.

Fix: Drop that sentence, or confirm the hooks are described in the changeset of the PR that introduced them.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Fixed in 7d84773: the stale MultiKeyEmitterBase sentence is removed from the changeset (the hooks came in with #82, which is already on main).


Generated by Claude Code

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Fixed (already in 7d84773 for the changeset). In c667c65 the same stale claim is also removed from docs/PROGRESS.md (TR5 entry): the listenerCount/onListenersChanged hooks come from #82 and this PR does not touch the emitter. The changeset now also describes the captureUserIdle opt-in.


Generated by Claude Code

…dy on main via #82)

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01A1UFAUHj4nozT7kLwBgMrC
@claude

claude Bot commented Oct 20, 2026 •

Copy link
Copy Markdown

Claude finished @bugseeai's task in 21s —— View job


Deep review of PR #83 (TR5 ProcessTracker), re-run on the latest push

I read the diff against origin/main, then trace-producers.ts and the wiring. I did not run tests, typecheck, lint or coverage, so the PR's gate numbers are still unverified by me.

What changed since my last review

The only new commit is 7d847734, which drops the stale MultiKeyEmitterBase sentence from .changeset/browser-process-tracker.md. The changeset now matches the diff, so the earlier P3 changeset finding is fixed. I found no new code changes to review.

Findings

No new findings. I rechecked the producer wiring in trace-producers.ts and found no new failure scenario:

  • Heap and lag producers: MemoryProducer and MainThreadLagProducer subscribe to heap and mainThreadLag and unsubscribe in onStop.
  • Shared tracker: all five producers share one per-window processTrackerFor instance, and env.sensors === false still returns only the battery producer.
  • Idle producers: MainThreadIdleProducer and UserIdleProducer are registered, and user_idle_state is emitted with emitIfChanged.
  • Stable closures: boundTo caches by (window, function), so a no-op relaunch keeps the same closures and does not restart armed stages.
  • Clock read: safePerformanceNow(env, undefined) no longer reports a hostile performance.now. The tracker reports it once (#clockFailing) when the lag or uptime stage activates. In createBrowserSystemTraceProducers the clock value only gates the UiTracker fps clock.

Still open from earlier rounds (not repeated as new)

  1. P2, consent scope of user_idle_state: the SDK reuses the page's idle-detection grant and the trace is on by default. Decide on an opt-in or a privacy-doc note before release.
  2. Held user_idle_state after a revoke: a producer that has already published keeps re-recording the last value. This is the owner decision the PR already flags, and storage_estimate has the same problem.
  3. P3, seed ordering in UserIdleProducer.onStart: on('userIdle') arms the stage before currentUserIdleFresh() runs, so the seed read returns undefined for a first subscriber. The detector's own publish still sets the trace, so this is optional.
  4. History: the branch still has the wip(trackers) commit 13b0f142 and a merge commit. Squash on merge if you want a clean main.

Summary

  1. Overall risk: Low.
  2. Merge recommendation: Approve once CI is green and the owner decisions on opt-in and retraction for user_idle_state are recorded. Nothing here is a correctness blocker.
  3. Most important items:
    • Settle the consent and opt-in question for user_idle_state.
    • Settle held-trace retraction after a revoke.
    • Confirm CI on 7d847734.
  4. Positives:
    • Absence is never sent as 0.
    • The SDK never prompts for idle detection.
    • Async continuations are guarded by their run.
    • Hostile host getters are read through hostRead and reported once.
    • The old per-signal trackers are removed with no dangling references.
    • The changeset is now accurate.
      · branch feat/process-tracker

…e (review P2)

The host page's idle-detection grant is not consent to record and upload
idle/locked state. UserIdleProducer is now built only for captureUserIdle:true
(default false, strict boolean, invalid reported) and still only produces a trace
when the permission is already granted (the SDK never prompts). Applies only to
user_idle_state. Also corrects the stale MultiKeyEmitterBase sentence in PROGRESS.md.

🤖 Generated with [Claude Code](https://claude.com/claude-code)
@claude

claude Bot commented Oct 20, 2026 •

Copy link
Copy Markdown

Claude finished @krassx's task in 30s —— View job


Deep review of PR #83 (TR5 ProcessTracker), re-run on the latest push

Scope: The only new commit since my last review is c667c65a (captureUserIdle opt-in). I read the changes in launch.ts (browser and WebView), trace-producers.ts, index.ts, the option definitions, BOOLEAN_OPTION in packages/core/src/options.ts, and the changeset/docs diff. I did not run tests, typecheck, lint or coverage, so the PR's gate numbers are still unverified by me.

Earlier findings: status

  • P2 consent scope of user_idle_state: addressed by c667c65a.
    • UserIdleProducer is now built only when env.captureUserIdle === true (trace-producers.ts, factory). Without the opt-in, the permission is never queried and no detector exists.
    • The option is captureUserIdle with default false and BOOLEAN_OPTION. normalizeBoolean accepts only real booleans, so 'true', 1 and null fall back to the default (off) and are reported. That is the right fail-closed behaviour for a behavioural-data option that is off by default.
    • The browser and WebView launches share one CAPTURE_USER_IDLE_OPTION_KEY, so they cannot drift apart. option-directions and relaunch-options tests were updated.
  • P3 stale changeset sentence: fixed in 7d847734. c667c65a also removes the same claim from docs/PROGRESS.md.
  • Still open, already disclosed and not repeated as new:
    • Held user_idle_state after a revoke. This is the owner decision the PR flags. The opt-in narrows who is exposed to it, but it does not remove it.
    • P3 seed ordering in UserIdleProducer.onStart. on('userIdle') arms the stage before currentUserIdleFresh() runs, so the seed read returns undefined for a first subscriber. The detector's own publish still sets the trace. Optional.
    • History. The branch still has the wip(trackers) commit and a merge commit. Squash on merge if you want a clean main.

New findings

None. Things I checked on the new commit:

  • Relaunch with the option toggled: captureUserIdle is passed in the env on every launch and the producers are rebuilt per launch. A true→false relaunch simply leaves the shared tracker without a userIdle listener, so the stage is torn down by #sync.
  • Position of the option in the env spread: captureUserIdle comes after ...browserTraceEnvFrom(...), so the helper cannot override it.
  • sensors: false path: this still returns only the battery producer, so the opt-in cannot re-enable idle tracking there.
  • Docs: the producer doc comment, the launch option JSDoc, the README and the changeset all say the same thing. The permission is still required and the SDK never prompts.

Summary

  1. Overall risk: Low.
  2. Merge recommendation: Approve once CI is green on c667c65a. The owner decision on retracting held traces after a revoke (shared with storage_estimate) can be recorded before release. It is not a correctness blocker, and the opt-in means only apps that enabled the trace are affected.
  3. Most important items:
    • Record the held-trace retraction decision.
    • Optionally swap the order of seed read and subscribe in UserIdleProducer.onStart.
    • Squash on merge.
  4. Positives:
    • The consent concern was handled with a strict, fail-closed, test-covered switch, and the SDK still never prompts.
    • One shared option key across browser and WebView.
    • Absence is never sent as 0, and async continuations stay run-guarded.
    • The changeset and docs now match the code.
      · branch feat/process-tracker

@krassx
krassx merged commit 7e1bbd4 into main Oct 20, 2026
8 checks passed
bugseeai pushed a commit that referenced this pull request Oct 20, 2026
…5/TR7 (#82/#83/#84 merged)

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01A1UFAUHj4nozT7kLwBgMrC
Sign up for free to 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.

3 participants