Repository navigation
docs(design): async origin capture for stackless rejections (BGSVIEWER-14) - #175
Conversation
…tions and a generic call-site wrapper 🤖 Generated with [Claude Code](https://claude.com/claude-code)
|
Warning Review limit reachedYou've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository. Next included review available in 31 minutes. View limit details
No actionable comments were generated in the recent review. Summary
Change: Other Merge Risk:
|
|
Claude finished @krassx's task in 59s —— View job Review of PR #175: async origin capture design (BGSVIEWER-14)This is a docs-only PR (a design doc and a probe script), so nothing was executed. I read both files and checked the doc's claims against
Findings (inline)
Summary
|
… of the call-site capture 🤖 Generated with [Claude Code](https://claude.com/claude-code)
|
Claude finished @krassx's task in 1m 14s —— View job Re-review of PR #175: async origin capture design (BGSVIEWER-14)This push added §4's "one factory" section and
Earlier findings: none fixed yetThe two new commits only added the factory and bench sections, so these still apply:
New findings (inline)
Summary
|
…ve circuit breaker 🤖 Generated with [Claude Code](https://claude.com/claude-code)
|
Claude finished @krassx's task in 56s —— View job Re-review of PR #175: async origin capture design (BGSVIEWER-14)This push added the "which listed methods can sit in a tight loop" class table and the adaptive circuit breaker. The PR is still docs and scripts only. I read the new sections and checked the claim about the SDK's own calls against Earlier findings, none fixed in this push (see the existing inline comments):
New findings (inline)
Summary
|
There was a problem hiding this comment.
Stale comment
Deep review — async origin capture design (BGSVIEWER-14)
Docs only; no runtime change. The diagnosis is right:
HTMLMediaElement.play()(and similar platform async failures) reject from a media/C++ task with a realDOMExceptionwhose.stackis missing, so nothing at detection time can recover a call site. The wrapper shape — call-timenew Error(), return the derived promise that rethrows — matches the existing fetch interceptor (packages/capture/src/fetch-interceptor.ts:387-433) and correctly preservesunhandledrejection. Not patching the globalPromiseis the right call. The generator + drift test + real-browser e2e plan is the right honesty mechanism.The consumption path as specified would not fix BGSVIEWER-14. That has to be corrected before this doc is used as the implementation spec.
Findings
P1 —
syntheticFramesis ignored for real Errors, and the motivating report is a real Error
buildCrashJsononly consultssyntheticFrameson the!(error instanceof Error)branch (packages/core/src/crash.ts:332-355). A tested invariant forbids putting them on an Error (crash.test.ts:187-193: “does NOT put synthetic frames on a real Error — it has its own”).logExceptionindependently setssyntheticFramestoundefinedwhenevererror instanceof Error(packages/core/src/client.ts:1757-1759). There is nocaptureException(removed in design v3; the public API islogException).BGSVIEWER-14’s reason is
NotSupportedError/DOMException, which is anError. Plumbing the WeakMap origin in throughsyntheticFramesis a silent no-op:crash.json.exception.framesstays[], and the worker still has no grouping location (crash.tscitesworker/crash/managed/common.py:88).The same consumers also drop the human-readable stack:
describeErrorinpackages/browser/src/detection-providers.ts:28-37returns{ summary }with nodescriptionwhen!value.stack. Even a future crash.json fix would leaverequest.json.descriptionempty unless that path is specified too.The seam that already runs on Error objects is
enrichFrames(receives the error plus whateverparseStackproduced —[]when.stackis missing). Node’scrashOfalready threads it for this reason (packages/node/src/detection-providers.ts:39-52); browsercrashOfdoes not.P2 —
remember()treats any engine frame as “usable”, so two measured rows stay unfixedThe predicate
!/ \s+at |@/.test(stack)refuses to store an origin as soon as the reason has anyat/@line. §2 listsWebAssembly.compile/instantiateandstructuredCloneas Chromium “1 frame, no user frame”. Those stacks match the regex, so origin is dropped and the report still has no call site.structuredCloneis also a sync throw, not a thenable; the generic wrapper returnspunchanged (or lets the throw escape) and never reachesremember.P2 — default set and
denyHotboth claimBody.*§2 measures
Body.jsonas stackless on Firefox. §7.2 defaults ON for the measured set. §4 cost #1 putsBlob/Body.*ondenyHot. One of those three statements has to give, or the Firefoxjson()case either stays broken or everyResponse#arrayBuffer/text/jsonpaysnew Error()+ an extra promise.P2 — “async origin” has no wire field;
hiddenwould undo grouping
CrashFrame/StackFramehave no origin/kind tag.toCrashFrameis a closed allow-list (crash.ts:141-168), so a tag left only onStackFrameis dropped. The only existing “not a real throw site” flag ishidden, which the worker skips when picking the crash location (crash.ts:17-20). Marking origin frameshiddenwould leave BGSVIEWER-14 ungrouped again. Viewer distinction needs a new optional field that grouping ignores; until then, origin frames must be ordinary visible user frames.Residual (not blocking)
probe.mjscreateRequires a hardcoded/Volumes/External2TB/...path, so the measured table cannot be re-run from this checkout. Plan §5 depends on turning that probe into e2e assertions.- WebKit is unmeasured (already an open question). Default-ON wrapping of
play()/clipboard/getUserMediashould stay gated until a matching Playwright WebKit build exists.- Electron renderer is covered if the interceptor is installed from
@bugsee/browserlaunchCore; no separate electron-renderer provider is required.Overall risk: Medium
Docs-only, so this does not break production. Implementing the doc as written would ship the wrappers and still upload
frames: []for the bug that triggered the work.Merge recommendation: Request changes
Most important to fix before this becomes the implementation spec:
- Consume origin via
enrichFrames(anddescribeError) on empty-stack Errors, notsyntheticFrames.- Define “usable frames” as a user/call-site frame, not any engine line; handle sync stackless throws separately or drop them from the default set.
- Resolve Body.json (default vs
denyHot) and add a crash.json field if the viewer is expected to render origin frames distinctly.Did not run the in-tree probe (host-absolute Playwright path). Read
buildCrashJson/logException/ browser+node detection providers / fetch interceptor against the doc.Sent by Cursor Automation: Bugsee code review
There was a problem hiding this comment.
Stale comment
Review: async origin capture design (BGSVIEWER-14)
Docs and measurement scripts only — no production behaviour change. I checked the plan against
packages/core/src/crash.ts,packages/core/src/client.ts(logException/describeError),packages/core/src/crash.test.ts(syntheticFramesis ignored for a realError),packages/browser/src/detection-providers.ts, and the existing fetch wrapper inpackages/capture/src/fetch-interceptor.ts. I did not re-runprobe.mjs/bench.mjs(both hard-code a host-absolute Playwright path).Findings
- P1 —
syntheticFramescannot attach origin frames to BGSVIEWER-14. The reason is aDOMException(Error); that seam is ignored for Errors.describeErrorandlogExceptionhave the same gap, and there is nocaptureException.- P2 —
remember's "usable frames" regex treats any engine frame (and any@in the message) as located, so the WASM / 1-frame rows never get an origin.- P2 — Default-on measured set and
denyHotboth claimBody.*/Body.json.- P2 —
createImageBitmapis default-on because it is stackless, then placed on the hot list whose breaker turns origin capture off in the frame-rate loops that justified calling it hot.Overall risk: Low
No runtime change. The risk is treating this doc as the implementation spec.
Merge recommendation: Request changes
Fix the P1 seam in the plan before this is the spec for step 2/3. Resolve the three P2s in the same pass so the default set is internally consistent.
Most important to fix
- Empty-stack Error fallback (
enrichFrames, or extend the Error branch). Same helper fordescribeError/logException. Drop thecaptureExceptionname.- Define "usable" via
parseStack(user/call-site frames, not "anyat/@").- One owner for
Body.json.- Do not default-on
createImageBitmapif the breaker will disable it on the hot path.Do not mark origin frames
hidden— the worker skipshiddenwhen picking the grouping location, which would restoreframes: []grouping for BGSVIEWER-14.Positives
- Measurement-first, with a Chromium/Firefox split and WebKit called out as unmeasured.
- The derived-promise trap is correct and matches the existing fetch interceptor (
call.then(..., err => { …; throw error })).- Not patching the global
Promiseis the right call.- One factory + drift test + real-browser e2e is the maintainable shape.
- Overhead was actually measured, including a hand-written control.
Sent by Cursor Automation: Bugsee code review
…s seam, arm-on-first-failure gating, target description 🤖 Generated with [Claude Code](https://claude.com/claude-code)
|
Claude finished @krassx's task in 1m 40s —— View job Re-review of PR #175: async origin capture design (BGSVIEWER-14), v2This push rewrote §4 into a two-mode wrapper with an Earlier findings now fixed in the doc
Still open from earlier reviews
New findings (inline)
Summary
|
There was a problem hiding this comment.
Stale comment
Review: async origin capture design v2 (BGSVIEWER-14)
Docs + measurement scripts only — no production behaviour change. HEAD
b18fe94avs last review (f837dc0f). Checked the v2 plan againstpackages/core/src/crash.ts(buildException/enrichFrames/toCrashFrame/syntheticFramesError invariant),packages/core/src/client.ts(logException,describeError,callSiteFrames),packages/browser/src/detection-providers.ts(browsercrashOfstill does not threadenrichFramesorevent.promise),packages/angular/src/error.ts,packages/capture/src/fetch-interceptor.ts, and the fetch/sha256/storage call sites. Did not re-runprobe.mjs/bench.mjs(host-absolute Playwright path). Did not run unit tests. CI lint on this branch is red (Biome on the new.mjsfiles; run41444497535, and the HEAD lint job).Prior findings at
f837dc0f
- P1
syntheticFrames/ empty-stackError— fully addressed. §4.5 now namesenrichFrames(crash.ts:196-206) and the browsercrashOf/describeErrorgaps correctly.buildExceptiondoes call the enricher whenparsed = []; that is the working seam.- P2
rememberregex — partially addressed. The/ \s+at |@/predicate is gone. “usable frames” is still undefined, and §4.5 applies origin only whenparsed.length === 0, so the Chromium WASM “1 frame, no user frame” and syncstructuredClonerows in §2 stay unfixed.- P2 Body.json vs
denyHot— not addressed. Still three owners (measured default-on, table off,denyHot).- P2
createImageBitmap+ breaker — partially addressed. The 200/s breaker is gone; arm-on-first-failure still drops the one-shot failure that §2 measured, and the table still says “see breaker”.- P2 origin wire /
hidden— partially addressed. Now an §8 open question. Residual: do not mark origin frameshidden— the worker skips those when picking the grouping location (crash.ts:17-20), which would restore empty grouping for BGSVIEWER-14.toCrashFrameis still a closed allow-list, so a tag left only onStackFrameis dropped.- P3 probe path — not addressed, and it now fails the lint gate.
New findings (v2)
The remaining P1 is the zone.js mode this revision added: patched-then lookup cannot retrieve origin or target state for awaited /
logExceptionpaths, which is how Angular writesplay().Overall risk: Medium
No runtime change. Implementing v2 as specified would fix bare Chromium
play()(rare set +enrichFrames) and would still shipframes: []with no element state for Angular/await video.play(), leak<video>nodes viabyReason, and contradict itself onBody.json/ one-shot decode.Merge recommendation: Request changes
Fix the patched-then lookup (or drop the false §4.3 fallback), pick one default policy per API, and make the probe lint/run from this repo before this is the implementation spec.
Most important to fix
- Patched-then must store
byReason(iframe-realm nativethen— already measured) so awaited and adapterlogExceptionpaths work. Write §10. There is nocaptureException.- Always capture for the measured-stackless set; arm-on-first-failure skips the failure that matters. Delete leftover “breaker” wording.
- One owner for
Body.json.- Do not pin
targetin aWeakMapkeyed by the exception; snapshot describer fields on reject.sanitizeUrlthe system-eventsrc.- Repo-relative Playwright require + Biome-clean scripts (CI lint is red).
Positives
- The v1 P1 (wrong seam) is actually fixed in the plan, with the right file:line citations.
- Two-mode wrapping is grounded in a real zone.js 0.14.10 measurement, not speculation; not patching
Promiseis still the right call.- Target/
thisas the stronger signal forplay()is the right product insight; the capture-phaseerrorlistener is a good complement ifsrcis scrubbed.callSiteFramesalready exists for wrapper-frame stripping on Chromium/Firefox/WebKit (stack.ts:272-276); the factory should use it.- Drift test + Chromium and Firefox e2e + a zone.js composition test is the right honesty mechanism.
Sent by Cursor Automation: Bugsee code review
…up everywhere, framework and custom-Promise survey 🤖 Generated with [Claude Code](https://claude.com/claude-code)
|
Claude finished @krassx's task in 1m 47s —— View job Re-review of PR #175 (async origin capture design, BGSVIEWER-14), push
|
🤖 Generated with [Claude Code](https://claude.com/claude-code)
|
Claude finished @krassx's task in 49s —— View job Re-review of PR #175 (async origin capture design, BGSVIEWER-14), pushes
|
There was a problem hiding this comment.
Stale comment
Review: biome exclude for research probes (BGSVIEWER-14)
Docs +
biome.jsononly. HEADaad867c0vs last review (3022f870). Two commits: excludedocs/design/async-origin-capturefrom biome, then formatbiome.json. I applied that config and ranbiome checkon the design doc and both.mjsfiles.What this push fixes
The mjs Biome errors no longer fail
pnpm lint— that part of the probe P2 is addressed. Excluding throwaway research scripts is a valid CI choice.The pattern is too broad: biome also ignores
docs/design/async-origin-capture.md(the spec). The host-absolute PlaywrightcreateRequireis unchanged, so the measured tables still cannot be reproduced from this checkout.Prior P2s — still open
origin: 'call'is decided but would be dropped bytoCrashFrame.- Arm-on-first-failure skips one-shot measured APIs;
Body.json/createImageBitmapstill have three owners.byReasonstill pinstarget.- Enricher
.causewalk still cannot recover axios/ky/ofetch origin.- Alpine / Preact-iso provider dedup is still not a property of the browser providers.
logExceptiondescribeErrorstill unnamed; §8 still says awaited zone chains cannot be located.Overall risk: Medium
No runtime change. CI lint is unblocked for the probes, at the cost of also dropping the spec from the format/lint gate. The implementation-spec gaps are unchanged.
Merge recommendation: Request changes
Narrow the ignore to the
.mjsfiles (ordocs/design/async-origin-capture/**) so the markdown spec stays in biome, and keep the remaining P2s in the doc (or as open questions).Most important to fix
- Ignore
docs/design/async-origin-capture/**or*.mjs, not the path prefix that also matches the.mdspec.- Repo-relative Playwright require (still host-absolute).
- Carry
origin: 'call'throughStackFrame→toCrashFrame→CrashFrame; do not sethidden.- Always capture the measured-stackless set; one policy per row; snapshot
targeton reject.- Drop the enricher
.causewalk; nameclient.tsdescribeError; delete the §8 zone leftover.Positives
Excluding research probes from the lint gate is the right kind of fix once the scripts are acknowledged as throwaway. Formatting
biome.jsonkeeps that file itself from failing the formatter.Sent by Cursor Automation: Bugsee code review
There was a problem hiding this comment.
Stale comment
Review: async origin capture design (biome exclude +
origin: 'call') — BGSVIEWER-14Docs + measurement scripts + a lint ignore. No production behaviour change. HEAD
aad867c0vs last reviewed unique design (0af17f19/ protocol decision3022f870). This push only excludesdocs/design/async-origin-capture/from Biome and formatsbiome.json.Checked the protocol decision and remaining claims against
packages/core/src/crash.ts(CrashFrame12–29,toCrashFrame141–168,buildException201–206),packages/core/src/stack.ts(StackFrame19–41),packages/core/src/client.ts(describeError171–184,logException1735),packages/browser/src/detection-providers.ts,packages/core/src/dedup.ts, andpackages/protocol/upload-contract.schema.json(no crash-frame schema). Ranpnpm lint: exit 0 (the two.mjsfiles are ignored). Did not re-runprobe.mjs/bench.mjs. Did not run unit tests.What this push fixes
The CI lint gate is green.
!docs/design/async-origin-capturematches how!samples/!packages/protocol/specalready work; I confirmed Biome ignoresprobe.mjsandbench.mjs. That was the merge-blocking half of the probe P2.The host-absolute Playwright
createRequire('/Volumes/External2TB/...')is unchanged, so the measured tables still cannot be re-run from this checkout.Prior findings
- P1
syntheticFrames/ patched-then — still fully addressed.- P2 Biome lint / CI red — fully addressed by the ignore.
- P2 probe path — partially addressed. Lint gone; scripts still not runnable here.
- P2
origin: 'call'copy path — not addressed. The field is decided (right call: nothidden, not a marker name) buttoCrashFramewould drop it, and §4.5 / plan step 7 still talk as if the protocol is open.- All other P2s from
0af17f19— not addressed (arm-on-first-failure, Body.json owners,byReasonpinstarget,.causewalk, Alpine/Preact-iso dedupe,describeError/ leftovercaptureException, §8 zone leftover).Overall risk: Medium
No runtime change. Implementing the doc as written would fix bare and awaited Chromium
play()viabyReason+enrichFrames, then droporigin: 'call'on the wire, skip one-shotdecode/FontFace.load/createImageBitmap/Body.json, leak<video>nodes, and leave AngularlogExceptiondescriptions empty.Merge recommendation: Request changes
The lint ignore is fine. The design is still not an implementation spec until the remaining P2s are fixed in the text or recorded as open questions.
Most important to fix
- Carry
origin: 'call'throughStackFrame→toCrashFrame→CrashFrame; do not sethidden. Point the “schema + drift test” at those TypeScript types (crash.json frames are not inupload-contract.schema.json). Update §4.5 and plan step 7.- Always capture the measured-stackless set; one
always/armed/offper row; delete leftover “breaker” /denyHotwording.- Snapshot describer fields on reject; do not pin
HTMLMediaElementinbyReason.- Drop the enricher-level
.causewalk (fetch is not wrapped;buildExceptionalready recurses). Tag the browser providers if Alpine / Preact-iso must not double-report.- Name
client.tsdescribeError; there is nocaptureException. Delete the §8 leftover that says awaited zone chains cannot be located.- Repo-relative Playwright require so the probes that justify default-on can actually run.
Positives
- Choosing an optional
origin: 'call'overhiddenor a synthetic marker name is the correct grouping-preserving call.- Ignoring research probes is consistent with
!packages/protocol/specand unblocks CI without touching production lint.- Pristine
then+ reason-keyed lookup still holds up as the wrapper shape for BGSVIEWER-14.Sent by Cursor Automation: Bugsee code review
There was a problem hiding this comment.
Stale comment
Review: biome ignore only — design unchanged (BGSVIEWER-14)
Docs + research scripts only. HEAD
aad867c0vs last review (3022f870). Unique delta isbiome.jsonexcludingdocs/design/async-origin-capture/(90b9cf7f+ format). I re-checkedCrashFrame/toCrashFrame(packages/core/src/crash.ts:12-29, 141-168),StackFrame(packages/core/src/stack.ts:19-41),buildException+applyDebugIds(crash.ts:201-206), browser and coredescribeError, AngularoriginalError, andcheckOrSetAlreadyCaught.pnpm exec biome check docs/design/async-origin-capturenow processes 0 files. Did not re-run probe/bench or unit tests. CI on this SHA was still in progress at review time.What this push fixes
The lint-gate half of the probe/Biome finding. Excluding the research directory matches
!samples/!packages/protocol/spec.pnpm lintis no longer blocked by those scripts.The host-absolute Playwright
createRequireis unchanged. The design document is identical to3022f870.Remaining spec gaps (still ship-wrong if implemented as written)
origin: 'call'is decided but has no field onStackFrame/CrashFrame, andtoCrashFrameis a closed allow-list that would drop it. §4.5 still says “protocol question”.- Arm-on-first-failure still skips the first (often only) rejection of the measured-stackless set.
Body.json/createImageBitmapstill have three owners (denyHot/ table off / measured on).byReasonstill pinstarget(the<video>) until the app drops the exception.- Enricher-level
.causewalk still cannot recover axios/ky/ofetch origin;buildExceptionalready recurses.- Alpine / Preact-iso “dedupe on reason identity” is not a property of the browser providers.
- Handled-path
describeErroris still the core one that returnsundefinedwhen!stack.- §8 leftover still says awaited zone chains cannot be located (contradicts §4.1).
- Zone wrapping (
DISABLE_WRAPPING…=false) still delivers a wrapper Error thatBugseeErrorHandlerdoes not unwrap.- New: origin frames injected by
enrichFramesnever getdebug_id—applyDebugIdsruns onparsedbefore the enricher.Overall risk: Medium
No runtime change. The biome ignore is correct. Implementing the rest of the doc as written still ships the same gaps as the last review, plus unsymbolicated origin frames.
Merge recommendation: Request changes
Land the lint exclusion. Name the
CrashFrame/toCrashFrame/applyDebugIds/ grouping contract next to theorigin: 'call'decision, and fix or explicitly open-question the remaining P2s before this is the implementation spec.Most important to fix
- Carry
origin: 'call'throughStackFrame→toCrashFrame→CrashFrame; do not sethidden. RunapplyDebugIdson frames after the enricher. Update §4.5 (it still says the protocol is open).- Always capture the measured-stackless set; one
always/armed/offper row.- Snapshot describer fields on reject; drop
targetfrombyReason.- Drop the enricher
.causewalk; tag browser providers if Alpine must not double-report; unwrap zone.rejectionon the Angular path.- Name
client.tsdescribeError; delete the §8 zone leftover.- Repo-relative Playwright (lint is done).
Positives
Excluding the research probes from Biome is the right way to unblock CI.
origin: 'call'(nothidden, not a marker name) is still the correct grouping-preserving choice.Sent by Cursor Automation: Bugsee code review
…ements, as-built notes, probe paths) The same doc and probe fixes as on the implementation branch (PR #176), so this PR's review findings are answered by the file it carries. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
|
Claude finished @krassx's task in 1m 16s —— View job Re-review of PR #175 (async origin capture design, BGSVIEWER-14), push
|
… status dependency on #176) 🤖 Generated with [Claude Code](https://claude.com/claude-code)
|
Claude encountered an error after 1s —— View job I'll analyze this and get back to you. |
1 similar comment
|
Claude encountered an error after 1s —— View job I'll analyze this and get back to you. |
There was a problem hiding this comment.
Stale comment
Review (4e90b8b)
docs(design): bring the design doc to its corrected stateanswers the P2s from the last pass. This is still a docs-only PR; the as-built claims are the spec for #176.Addressed
- Probe/bench Playwright path is repo-relative (
import.meta.url).origin: 'call'is specified onStackFrame/CrashFrame/toCrashFrame; grouping is the first non-hiddenuserframe; enricher frames getdebug_idinbuildCrashJson(§4.5, §13).- Arm-on-first-failure is an accepted first-report gap;
Body.jsonis deliberatelyoff;createImageBitmapisarmed; no separatedenyHot.- Retention of
targetfor the reason's lifetime is stated (§4.2, §11)..causewalk is scoped to ky/ofetch; Alpine/Preact-iso double-report is called out as independent.- Both
describeErrors are named; the leftover zone.js open question is marked RESOLVED.Residual (P3, docs consistency after a “corrected state” pass)
- §10.4 Astro row is still GAP; §10.5 says FIXED.
- §10.6 still says “WebKit untested”; §7/§8/§12 say it was measured in e2e.
- Zone wrapping (
DISABLE_WRAPPING…=false) still lists only “pristine then”. The event reason is zone's{rejection, …}object, so a by-reason lookup of theDOMExceptionmisses unless.rejectionis unwrapped. Default zone does not wrap; this is the opt-in row.Also stale, not re-filed: §4.5 “passes neither today”; factory row
{ hot? }/capture: false;captureExceptionin §10.3.Risk: Low. Merge: approve with the three leftover contradictions cleaned up, or land as-is if #176 remains the source of truth for as-built notes.
Sent by Cursor Automation: Bugsee code review
There was a problem hiding this comment.
Stale comment
Re-review of PR #175 at 4e90b8b (prior aad867c). Unique +739/−1 vs
main. Still design + probes only; behaviour lives on PR #176.This push vs last review: the design is rewritten as the as-built record of #176 (§11–§14), and the probe/bench
createRequirepaths plus the 5 s timeout label are fixed.Prior P2s, all addressed in this file:
origin: 'call'is onStackFrame/CrashFrame/toCrashFrame; enricher frames getdebug_id(§4.5, §13). Matches #176crash.ts.- Arming is
recorder.arm()from the enricher on any report path; first armed rejection has no call site (accepted); rare rows arealways(§4.4).Body.jsonis deliberately off;createImageBitmapisarmed; nodenyHotlist.- Retention is keyed by the reason, not settle; target pin documented and accepted (§4.2, §11).
.causewalk scoped to ky/ofetch; axios XHR / Mithril are gaps (§4.5, §10.4).- Alpine / Preact-iso double events called out of scope (§10.5).
- Both
describeErrors anddescribeTargetnamed; adapters go throughlogException(§4.5, §7.5).- Zone leftover resolved by reason identity (§8). Playwright paths are repo-relative;
noEventWithin5sreplacesnoUnhandledRejection.No remaining P0–P2. Two P3 leftovers from the "corrected state" rewrite (inline).
Residual (not re-filed): Firefox
play()Target line is still read at report time, so anunhandledrejectionreport can seeerror = null(Firefox already has a call site, §2). Zone wrapping-on (DISABLE_WRAPPING…=false) still does not walk.rejection. §4.5 still says the browser provider "passes neither today." Nuxt path still citesentry.js:43(packages/nuxt/src/client.ts:49isinstallBugseeErrorHandler).Risk: Low. Merge: approve.
Sent by Cursor Automation: Bugsee code review
There was a problem hiding this comment.
Stale comment
Review (251aa01)
The leftover-contradiction pass lands. Status now says as-built paths exist after #176 and that #176 should merge first. §10.4 Astro is FIXED (matches §10.5). §2 and §10.6 no longer say WebKit is untested. §4.5 no longer says the browser provider “passes neither today.”
Still open (P3): the zone.js
DISABLE_WRAPPING_UNCAUGHT_PROMISE_REJECTION=falserow still lists only “pristine then.” In that opt-in mode zone replacesevent.reasonwith{rejection, promise, zone, task}, sooriginOf(event.reason)misses theDOMExceptionunless.rejectionis unwrapped. Default zone does not wrap. Same fix as last pass: unwrap, or mark the row as an accepted gap like Redux.Risk: Low. Merge: approve (land #176 first).
Sent by Cursor Automation: Bugsee code review
There was a problem hiding this comment.
Stale comment
Re-review of PR #175 at 251aa01 (prior 4e90b8b). Docs-only; +10/−8 in the design doc. Land #176 first, then this — the status line now says so, and the PR description matches.
This push vs last review: the two P3 leftovers are gone. Astro’s survey row is FIXED (matches §10.5). §2/§10.6 no longer call WebKit untested. §4.5 no longer says the browser provider “passes neither today.” Claude’s dead-reference P3 is addressed by the #176-first status line. Astro’s launch-window queue is closed in
registerClient’sfinally.No remaining P0–P2.
Residual (not re-filed): zone wrapping-on (
DISABLE_WRAPPING_UNCAUGHT_PROMISE_REJECTION=false) still lists only “pristine then.” The event reason is zone’s{rejection, …}object, sooriginOf(event.reason)misses unless.rejectionis unwrapped. Default zone does not wrap.Risk: Low. Merge: approve (#176 first).
Sent by Cursor Automation: Bugsee code review
There was a problem hiding this comment.
Stale comment
Re-review of 251aa01 (docs only; unique +741/−1 vs
bf620ab3). Two commits since last look (aad867c0) bring the design to the as-built state of #176.Prior findings:
origin: 'call'onStackFrame/CrashFrame/toCrashFrame,applyDebugIdsafterenrichFrames, Body.json off, target retention,.causewalk limits, Alpine/Preact-iso not-deduped, bothdescribeErrors, zone leftover, Angular default wrapping, probecreateRequire(import.meta.url), biome/**— fully_addressed. Arm-on-first-failure cost is accepted and documented; the frequency-table Default column is the leftover (partially_addressed).Checked against surrounding code on this branch (
crash.tsstill stampsapplyDebugIdsbeforeenrichFrames; browsercrashOfstill has no enricher) and againstorigin/async-origin-impl(packages/capture/src/async-origin-table.ts,async-origin.tsarm()installs everyarmedrow, WebKit play() e2e requiresorigin: 'call'on every frame). Did not run tests.Findings
P2 — Hot-class Default column still says off; as-built and the rest of this doc say
armed
docs/design/async-origin-capture.md:318vs:237/:323and #176ASYNC_ORIGIN_TABLE.P2 — WebKit play() is not “like Firefox”
:51vs §2 Firefoxplay(has call site) and #176 WebKit e2e (frames.every(origin === 'call')).Overall
- Risk: Low (this PR has no production change; leftover contradictions would mis-map #176)
- Merge: request changes
- Fix first: Default column + factory leftovers (
{ target, methods, hot? }, 36 B token,capture: false); WebKit play() characterization- Positive: Status line now names the #176 landing order; Body.json gap, WeakMap retention, enricher arming, Astro launch-window, and the accepted first-failure hole are stated honestly
Sent by Cursor Automation: Bugsee code review
… armed, factory row shape, zone flag gap) 🤖 Generated with [Claude Code](https://claude.com/claude-code)
|
Claude encountered an error after 1s —— View job I'll analyze this and get back to you. |
There was a problem hiding this comment.
Stale comment
Review (650f234)
The two P2 leftovers from 251aa01 are gone, and so is the wrapping-row P3.
- WebKit
play()is stated as stackless (Chromium-like); the text now warns not to read it as Firefox’splay()row.- Hot class Default is
armed, matching §4.4 and the as-built table. Factory row is{ iface, methods, mode, targets?, why }; the 36 B token andcapture: false/ sampling are gone.- Zone
DISABLE_WRAPPING…=falseis an accepted gap:event.reasonis{rejection, …}, by-reason lookup misses.No remaining P0–P3. Residual: the Rare column still says on while the table’s mode is
always(same policy, different word).Risk: Low. Merge: approve. Land #176 first, then this.
Sent by Cursor Automation: Bugsee code review
There was a problem hiding this comment.
Stale comment
Review (650f234)
Docs-only. Unique vs last look (
251aa01a) is +7/−8 indocs/design/async-origin-capture.md. Behaviour still lives on PR #176 (open; land that first). I re-read the full design, the probes, and the as-built table/wrapper/enricher/e2e onasync-origin-impl— not the 15-line delta in isolation.This push vs last review: the two P2s close.
- Hot-class Default is now
armed(§4.4), includingcreateImageBitmap. That matches#176ASYNC_ORIGIN_TABLE:Scheduler.postTask/yield,FileSystemWritableFileStream.*,GPUBuffer.mapAsync,GPUQueue.onSubmittedWorkDone,GPUDevice.popErrorScope,ImageCapture.grabFrame,ImageDecoder.decode,VideoFrame.copyTo,Video/AudioDecoder/Encoder.flush,OffscreenCanvas.convertToBlob,createImageBitmapare allmode: 'armed'. Row shape is{ iface, methods, mode, targets?, why }. The 36 B token andcapture: false/ sampling leftovers are gone.- §2 no longer says WebKit “behaves like Firefox”.
play()is stackless there (Chromium-like:#176WebKit e2e asserts everycrash.jsonframeorigin: 'call', which the enricher only injects when the engine stack parsed to nothing). Tail-call elimination stays in §12.- Zone
__zone_symbol__DISABLE_WRAPPING_UNCAUGHT_PROMISE_REJECTION=falsenow records the accepted miss:event.reasonis{rejection, promise, zone, task};originOfwalks.causeonly.Prior P2s (hot Default / WebKit play): fully_addressed.
No unique P0–P3. I looked for leftover as-built contradictions (factory options,
denyHot, Body.json policy, arm-all-at-once,enrichFramesvssyntheticFrames, Astro FIXED) and did not find a new one that would ship the wrong wrap set or drop Safariplay().Residual (not filed):
wrapPromiseMethodis still the design name; as-built isinstallAsyncOrigins+ innerwrap(). Bursty Default still says “armed for the measured-stackless ones” while the cited table arms the whole bursty set. Both are naming/summary leftovers, not a wrong policy.Did not run tests (docs + research scripts only).
Overall risk: Low
Merge recommendation: approve — land #176 first, then this.
Most important issues to fix
None on this PR.
Positives
The class table, factory row, and WebKit paragraph now match the code and the e2e instead of the v1 leftovers. That is the job of an as-built design record.
Sent by Cursor Automation: Bugsee code review
…#176) Async origin capture: wrap promise-returning platform APIs so a stackless async rejection (HTMLMediaElement.play() NotSupportedError, BGSVIEWER-14) is reported with the call site as its primary frames (origin: 'call'), the target element description, and a grouping signature. Browser SDK only; verified in real Chromium, Firefox and WebKit, with zone.js, Bluebird, core-js, React and Vue. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
|
Claude encountered an error after 1s —— View job I'll analyze this and get back to you. |
There was a problem hiding this comment.
Stale comment
Review (1e6c9f5)
#176 merged into this branch (
baseRefName: async-origin-design). The squash1e6c9f5ahas the same tree as last-reviewed #176 HEAD4446468a(emptygit diff). No new production delta on this push.This PR is now the vehicle that lands the as-built design and the implementation onto
main. Prior reviews of both sides stand:
- Design (650f234): hot class
armed, WebKitplay()stackless, zone wrapping accepted.- Implementation (#176):
enrichFrames+origin: 'call'+debug_id,arm()from every report path, reason-keyed lookup, Astro hydration window, Playwright Chromium/Firefox/WebKit e2e.No remaining P0–P3 unique to this PR. Residuals that already shipped with #176 (not re-filed):
registerClient({ document: A })thenrelaunch({ window })can dropdocument; coredescribeErrorstillinstanceof Error;AsyncOrigin.skipJSDoc vs runtime.Risk: Low. Merge: approve onto
main.Sent by Cursor Automation: Bugsee code review
There was a problem hiding this comment.
Review (1e6c9f5) — implementation now lives on this PR
HEAD squash-merges #176 onto the design branch. Unique vs last look (650f234f) is +6334/−134 across 53 files: this is no longer docs-only. The PR body still says “no behaviour change; land #176 first” — that is stale. Merging this now ships default-on prototype wrapping for the measured stackless set.
Checked against the as-built capture path (installAsyncOrigins, createAsyncOriginReporting, browser crashOf/logException, buildCrashJson enrichFrames), the table, Astro hydration, and prior #176 residuals. Did not run tests or CI this pass.
Prior docs P2s (WebKit play(), hot-class armed, factory row shape, zone .rejection) remain fully_addressed in the design doc.
P2 — wraps are installed before a published client exists, and a retry cannot rebind them
Location: packages/browser/src/launch.ts:1031 (no try/finally); packages/capture/src/async-origin.ts:310 (MARK skip)
Problem: installAsyncOrigins patches HTMLMediaElement.play (and the other always rows) as soon as buildLaunch reaches it, long before setCarrierClient. Wrappers are marked with Symbol.for('bugsee.asyncOrigin'). stop() — the only restorer — is registered later as a detection provider (launch.ts:1405) and on the public client (:1662). If anything throws in that window, or a second launch() races the first, stop() never runs. A later launch() sees MARK and skips wrapping (installed: []). Live play() still hits the orphaned wrapper/recorder; detection/logException use a new recorder and get no origin.
The window is large: wrap is at 1031, publish at 1705. A concrete throw site in between is the unguarded manifest loop (:1119-1120).
Impact: BGSVIEWER-14 stays fixed only until the first failed or overlapping launch. After that, stackless play() reports frames: [] again, until a full reload. Silent — no onError.
Scenario:
launch()installs wraps.- A contributed service manifest throws (or any throw after 1031), or two
launch()calls overlap acrossawait resolveBrowserDeviceId(:750) so both pass the singleton check. - App retries
launch()(SPA, Strict Mode remount,launch().catch(() => launch())). play()is still wrapped by the dead recorder; the published client’s enricher cannot find the origin.
Fix: Tie wrapper lifetime to a successfully published client: try/finally { asyncOrigins?.stop() } on any path that does not hand off the client (including launchCore's catch when the carrier slot is still empty). Optionally have wrap() treat a MARKed method as rebindable to the new recorder instead of skip-only. Add a test: throw after install, assert play is restored and a second launch re-wraps and reports origin: 'call'.
Residuals (not re-filed)
logExceptionof a non-Error always suppliessyntheticFrames(client.ts:1763-1776); the real enricher then treats those as “already has frames” and never looks up origin. Same-realmDOMExceptionis anError, sounhandledrejection/ adapterlogExceptionofplay()still work. Cross-realm would also not be wrapped (iframe has its own prototype).- Astro
lastHydrationis written beforestart()(client.ts:67): a failedrelaunch({ document: B })then a plainrelaunch()listens on B while corelastLaunchstays A. Losses only in the relaunch window. registerClient({ document: A })thenrelaunch({ window: W })can split hydration target vs launch document (sibling P3 on #176).- Armed-row first failure has no call site (documented accepted price).
AsyncOrigin.skipJSDoc still says 1; Firefox/Safari path uses 2.
- Overall risk: Medium — default-on prototype wrapping is the right design for BGSVIEWER-14; the lifecycle hole can silently undo it after a failed/racy launch.
- Merge recommendation: Request changes
- Most important to fix: Restore (or rebind) async-origin wraps whenever launch does not publish a client.
- Positives:
enrichFramesis the correct seam for a realError; one definition of “has frames” (parseStack.length > 0);origin: 'call'+debug_idre-stamp; arming from the enricher covers React/Vue/logException; pristinethen/ zone.js path; Chromium/Firefox/WebKit e2e forplay();stop()/KILL_SDKrestore when launch actually succeeds.
Sent by Cursor Automation: Bugsee code review
|
|
||
| // Async origin capture: wrap the page window's promise-returning platform methods BEFORE the application's first call, so a | ||
| // stackless rejection (BGSVIEWER-14) can be reported with where it was called from. Stopped with the client. | ||
| const asyncOrigins = resolveFailOnBoolean(options.captureAsyncOrigins, true) |
There was a problem hiding this comment.
P2 — wraps installed here are not restored if launch fails (or races) before the client is published.
installAsyncOrigins patches HTMLMediaElement.prototype.play (and the other always rows) immediately. Wrappers are marked Symbol.for('bugsee.asyncOrigin') (async-origin.ts:310). stop() is the only restorer, and it is not hooked until the detection provider (:1405) and the public client (:1662). setCarrierClient is at :1705.
If anything throws in that window — the unguarded getServiceManifests loop at :1119 is a concrete site — or a second launch() overlaps across await resolveBrowserDeviceId (:750) so both pass the singleton check, stop() never runs. A retry sees MARK and skips wrapping (installed: []). Live play() still hits the orphaned wrapper; the new recorder has no origin. BGSVIEWER-14 reports frames: [] again until a full reload, with no onError.
Wrap this in try/finally { asyncOrigins?.stop() } on any path that does not publish the client, and add a test: throw after install, assert the method is restored and a second launch re-wraps.


Design doc, measurements and probes only: no behaviour change in this PR. The doc has since been corrected against the review findings and brought to its as-built state (it records what #176 built and what real engines taught), so it cites files that exist once #176 has merged. Land #176 first, then this.
BGSVIEWER-14 is an unhandled
NotSupportedError: The element has no supported sources.with zero frames. Measured in headless Chromium 156 and Firefox 151 (docs/design/async-origin-capture/probe.mjs): platform promises that fail asynchronously reject with no stack in Chromium (and fewer in Firefox); user-codeErrorrejections already carry stacks, so the globalPromiseneeds no patching. The doc lists the measured stackless set, the 187 candidate methods fromlib.dom.d.ts, a generic call-site wrapper (with theunhandledrejection-preserving derived-promise trap), costs/risks, and a plan.WebKit was not part of the original measurements; it was run later in the e2e (design doc §12).
🤖 Generated with Claude Code