Skip to content

✨ Persist tracking consent for crash recovery - #233

Open
rgaignault wants to merge 6 commits into
mainfrom
romanG/rum-15526-context-history
Open

rgaignault wants to merge 6 commits into
mainfrom
romanG/rum-15526-context-history

Conversation

@rgaignault

@rgaignault rgaignault commented Oct 2, 2026 •

Copy link
Copy Markdown
Collaborator

Motivation

A crash is reported at the next launch, with the session, view and customer context saved before it happened. That context does not show whether the crash was captured with consent, so a crash from a refused or undecided period could be sent once tracking is granted again. Part of RUM-15526.

Changes

  • Persisted consent history: TrackingConsentManager stores its timestamped history in a DiskValueHistory, like the session and view histories.
  • Assembly check: a RUM format hook discards events whose start time falls in a refused period, or outside the known history. A pending period takes the decision that ended it; events of a period still undecided are left to the batch storage.
  • Restart: a pending period left undecided by the previous process is recorded as refused, as its pending batches are deleted at startup.
  • Recovered crashes: no specific handling. They go through the same check, then are stored according to the current consent like other events.

Consent changes are still written asynchronously: a crash right after a refusal, before it reaches disk, is accepted at the next launch.

Test instructions

  • yarn vitest run
  • yarn tsc -p src --noEmit
  • yarn test:e2e: the crash scenarios cover restart recovery with the default granted consent.

Checklist

  • Tested locally (playground)
  • Added unit tests for this change.
  • Added e2e/integration tests for this change.
  • Updated related documentation.
  • Agentic code review findings addressed or explicitly dismissed.

@rgaignault
rgaignault added this pull request to stack #236 October 2, 2026 12:17
@rgaignault
rgaignault marked this pull request as ready for review October 2, 2026 12:22
@rgaignault
rgaignault requested a review from a team as a code owner October 2, 2026 12:22
@rgaignault

Copy link
Copy Markdown
Collaborator Author

@codex review

@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Oct 2, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-10-08T01:03:41.714878Z ab08ef2 New commits
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@cursor cursor Bot left a comment •

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Stale comment

PR Review — Quality: 5.0 / 5 · Review load: Focused (~30min–1h)

Approve. Pending context stays in memory, refusal restores the last authorized snapshot, and a grant writes that history with the refused interval left empty. Existing session, view, and customer-context storage is unchanged on this branch.

Customer impact: Low — DiskValueHistory still records session, view, and customer context on every change. Nothing calls ContextHistoryFactory, so this history is written only after a later change constructs it.

Concern
Consent transitions, refusal rollback, and restart Risky
Ordered JSON disk writes Routine

Quality — 5.0: The transitions are correct: leaving granted closes the active entry and snapshots it before a pending value is reopened in memory, and refusal puts that snapshot back. Tests match the risk — same-timestamp refusal and grant, restart with the previous process's value closed, collector ordering, and a serialization failure isolated to one history — and DiskStorage covers ordered writes and a failed write. The architecture section states this contract. The design holds up: one factory subscription updates every history before later collectors run, which is the boundary session and view wiring can build on.

Review load — Focused (~30min–1h): The risky read is the consent state machine and what survives refusal or restart. The disk queue is a short routine piece in the same style as DiskValueHistory. Both sit in ✨ Add consent-aware context history.

Open in Web View Automation 

Sent by Cursor Automation: electron-sdk reviews

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 0e26c0c4b2

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/domain/tracking-consent/TrackingConsentHistory.ts Outdated
Comment thread src/domain/tracking-consent/TrackingConsentHistory.ts Outdated
Comment thread src/domain/tracking-consent/TrackingConsentHistory.ts Outdated

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

PR Review — Quality: 5.0 / 5 · Review load: Focused (~30–45min)

This is a correct, well-tested slice, and I would merge it as-is. TrackingConsentHistory keeps undecided changes in memory, writes them on grant, and on refusal restores the last authorized snapshot, including the same-timestamp and previous-process cases. ContextHistoryFactory applies that transition for every history before a later subscriber runs. GitLab e2e and integration were still running when I posted. Approve.

Customer impact: Low — init still records session, view, and customer context through DiskValueHistory. These types affect disk and events only once a later change constructs ContextHistoryFactory.

Concern
Consent-aware context history (transitions, snapshot persistence, subscription order) Risky

Quality — 5.0: The transition rules match the consent model in the description and in docs/ARCHITECTURE.md: a refusal leaves the refused interval empty, a grant keeps the latest configured value for the new period, and a restart leaves the previous process out of the current context. The factory specs cover grant, refusal, same-timestamp boundaries, a consent change during load, and a serialization failure on one history while another still commits; DiskStorage covers ordered snapshots and a failed write. The split is sound: DiskStorage is only a JSON queue, TimeStampValueHistory stays the interval index, and the factory owns the single consent subscription so a collector cannot observe a half-applied transition.

Review load — Focused (~30–45min): One risky boundary: which intervals are authorized, which stay in memory, and the guarantee that every history has applied the transition before a collector callback. The write queue and replaceEntries belong to that same behavior. The one commit carries the whole boundary.

Open in Web View Automation 

Sent by Cursor Automation: electron-sdk reviews

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 290b119a21

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/domain/tracking-consent/TrackingConsentHistory.ts Outdated

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: d5cbbf1c77

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/domain/tracking-consent/TrackingConsentHistory.ts Outdated
Comment thread src/domain/tracking-consent/ContextHistoryFactory.ts Outdated
Comment thread src/tools/DiskStorage.ts Outdated
@maciejburda
maciejburda requested a review from bcaudan October 5, 2026 07:11
Comment thread docs/ARCHITECTURE.md Outdated
Comment thread docs/ARCHITECTURE.md Outdated
Comment thread docs/ARCHITECTURE.md Outdated
@rgaignault rgaignault changed the title ✨ Add consent-aware context history ✨ Persist tracking consent for crash recovery Oct 7, 2026
@rgaignault
rgaignault requested a review from bcaudan October 7, 2026 12:09

@bcaudan bcaudan left a comment •

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Looking at what we want to do and the existing patterns in the code base, I would expect:

  • TrackingConsentManager gains a contextHistory to store the consent states by timestamp
  • A hook is added to add a consent check at assembly based on the event start time, consistent with how we add the attributes on the crash

If it is not that simple, could we still go into that direction and see what needs to be adjusted to support this use case?

Persist the consent history like the session and view histories, and
discard RUM events whose start time falls in a refused or unknown
period. A pending period takes the decision that ended it, and a period
left undecided by the previous process counts as refused.

Recovered crashes no longer need a dedicated check or storage override:
they are stored according to the current consent like other events.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: ab08ef25ea

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +32 to +35
const history = await DiskValueHistory.init<TrackingConsent>({
filePath: path.join(app.getPath('userData'), TRACKING_CONSENT_HISTORY_FILE_NAME),
expireDelay: SESSION_TIME_OUT_DELAY,
});

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 Badge Treat malformed consent history as absent

When _dd_tracking_consent_history contains syntactically valid JSON such as [null], this new call reaches DiskValueHistory.init(), whose restore loop dereferences entry.endTime outside its read/parse try block (src/tools/DiskValueHistory.ts:49-55). The resulting exception rejects the top-level SDK init() instead of falling back to an empty consent history. The current revision therefore retains the earlier malformed-history failure through the newly introduced DiskValueHistory path even though the intermediate TrackingConsentHistory implementation is gone; validate restored entries or catch restoration errors before constructing the manager.

Useful? React with 👍 / 👎.

return consent === 'granted' || consent === 'pending' ? SKIPPED : DISCARDED;
});

return manager;

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 Badge Await the initial consent snapshot before returning

On a fresh launch, constructing the manager only queues the initial consent snapshot through DiskValueHistory.add(); this method returns before its fs.writeFile promise settles, and init() provides no flush before returning. If the native process crashes shortly after the SDK reports successful initialization, the history file can still be missing or incomplete, so the next launch finds no consent interval covering that crash and discards the recovered report. Await the initial snapshot before completing initialization so the crash-recovery feature is effective immediately.

Useful? React with 👍 / 👎.

Comment on lines +43 to +46
hooks.registerRum(({ startTime }) => {
// Events of an undecided pending period are held by the batch storage until its decision.
const consent = manager.getDecisionAt(startTime);
return consent === 'granted' || consent === 'pending' ? SKIPPED : DISCARDED;

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 Badge Rotate pre-init renderer views when consent history starts

When the Browser RUM SDK is already running in a window before a deferred main-process init(), the active renderer view keeps a date from before this manager was created. RendererPipeline passes that unchanged date as startTime for every later update of the view, so this callback finds no covering consent entry and discards every update even after initialization; meanwhile post-init actions and errors can still be uploaded with that view ID, leaving them attached to a view document that never arrives until navigation or session renewal creates a new view. The documented pre-init bridge supports such existing windows, so initialization needs to rotate/rebase their active views or otherwise establish a consent boundary they can use.

Useful? React with 👍 / 👎.

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.

2 participants