Skip to content

Commit f3a1dde

Browse files
committed
docs(design): what the backend still needs before the JS SDK beta
A read-only audit of appserver, worker and viewer `origin/main` against the SDK's wire output. Most JS support is merged; what remains is narrower but includes three report-losing blockers: mobile-browser reports are checked against the native iOS/Android version floor, a prerelease version fails the JS floor (and the SDK then deletes the report), and JS reports without a crash.json all merge into one `no-crash-file` issue. Records the decisions: beta reports 0.1.0-beta.N, Electron runtime symbols deferred, the viewer's production gate opens in its PR. SDK-side findings are listed as the release track, not part of these PRs. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
1 parent 0c32485 commit f3a1dde

1 file changed

Lines changed: 224 additions & 0 deletions

File tree

Lines changed: 224 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,224 @@
1+
# Backend readiness for the JavaScript SDK beta
2+
3+
Status: **Design — approved decisions recorded, not built.** 2026-09-29.
4+
5+
The backend already carries most of the JavaScript support: the `javascript` app type and its 27
6+
target subtypes, per-runtime version floors, symbol routing, the `js` build format, sourcemap
7+
symbolication by debug-id, native minidumps, rrweb replay, runtime labels, frame variables and
8+
source context. That work is described in `javascript-application-type.md`,
9+
`appserver-javascript-support.md` and `electron-native-crashes.md`.
10+
11+
This document covers what is still missing before a public beta can send reports to production
12+
and have them accepted, processed and shown. It came out of a read-only audit of each repo's
13+
`origin/main` against the SDK's `main` (`0c32485`), with every load-bearing finding re-checked in
14+
source.
15+
16+
## Scope
17+
18+
In: the appserver, the background worker and the viewer. One worktree and one PR per repo, under
19+
`/Volumes/External2TB/Projects/Bugsee/_worktrees/{appserver,worker,viewer}-js-beta`, branch
20+
`feat/js-beta-readiness`, based on each repo's `origin/main`.
21+
22+
Out, and tracked as the SDK release track (see the last section): anything that has to change in
23+
this repo — publishing, the reported version string, missing `crash.json` on some report kinds,
24+
the browser `user-agent` header, the empty OS value, the Electron video attributes.
25+
26+
## Decisions
27+
28+
| # | Decision | Chosen |
29+
|---|---|---|
30+
| D1 | Version the beta reports | `0.1.0-beta.N`. The appserver floor must admit prereleases. |
31+
| D2 | Electron runtime system symbols (Electron/V8/Node frames in native crashes) | **Deferred.** Beta limitation; the viewer says so instead of asking for an upload. |
32+
| D3 | When the viewer's production gate opens | **In this PR.** The viewer reaches production only through a manual deploy, so the launch is that deploy. |
33+
| — | Line-level secret scrubbing of source context | **Not revived.** Closed as a product decision on 2026-09-06 (worker #53, javascript #4); `source_context_disabled` is the mitigation. |
34+
35+
## appserver
36+
37+
### A1 — stop rejecting reports from mobile browsers (blocker)
38+
39+
Since `e53b58f` the browser tier reports the operating system in `platform.type`, so a mobile
40+
browser sends `ios` or `android` (`packages/browser/src/user-agent.ts`).
41+
`issue.service.js:1050-1064` treats any Apple or Android `platform.type` as a native mobile SDK
42+
and checks the reported version against the iOS (1.27.0) or Android (1.19.11) floor. A JS SDK at
43+
0.1.0 fails and gets `IssueReportedByUnsupportedSdk` (12007). The SDK does not know 12007, treats it
44+
as transient, and retries the report at every launch without end.
45+
46+
Change: return early from that check when `env.sdk.type === 'javascript'`. The JS floor is
47+
enforced at session start by `utils.js isSupportedSdkVersion`, keyed by the runtime, not the OS.
48+
49+
### A2 — admit prerelease versions (blocker)
50+
51+
`config/default.js:156-161` sets every JS runtime's `minimum` to `0.1.0`, and
52+
`semver.gt('0.1.0', '0.1.0-beta.1')` is true, so a beta session fails with `UnsupportedSdkError`
53+
(99098). The SDK classes 99098 as permanent and deletes the report.
54+
55+
Change: every JS runtime block gets `minimum: '0.0.0-0'` (the lowest version semver can express, so
56+
any prerelease passes), and `current`/`old` become `0.1.0-beta.1`. Those two are release-tunable and
57+
move with each beta.
58+
59+
### A3 — send a retryable error under the rate limit (major)
60+
61+
When the issue rate limit trips, `issue.controller.js:84,96` sends the retryable `ServerTooBusy`
62+
only if `errors/index.js isServerTooBusySupported` says so. That function answers for iOS and
63+
Android only, so a JS client gets `TooManySimilarCrashes` (12004), which the SDK treats as
64+
permanent. Error storms on a busy site lose their reports.
65+
66+
Change: `isServerTooBusySupported` returns `true` when `environment.sdk.type === 'javascript'`. The
67+
JS SDK has handled `ServerTooBusy` since its first version, so there is no version floor.
68+
69+
### A4 — list the JS resource types for the MCP (minor)
70+
71+
`mcp/tools/issue.get-resource.js:43-62` omits `replay`, `profile` and `video.aux`, so an agent cannot
72+
fetch a web session's replay. Add them to the enum and its description.
73+
74+
## worker
75+
76+
### W1 — symbolicate from `trace` (blocker + major)
77+
78+
Commit `6b99a7a` on `feat/js-context-diagnostic` was pushed after PR #48 merged and never reached
79+
`main`. It reads each frame's position from the SDK's `trace` instead of from `data`, which fixes two
80+
bugs the audit reproduced:
81+
82+
- **Anonymous frames never symbolicate.** A frame with no function name carries no `data.member`,
83+
so the worker builds `@line:col`; `symbolfiles/sourcemap.py:17`'s `^(.+)@(\d+):(\d+)$` needs a
84+
non-empty name and silently misses. Anonymous frames are the norm in minified code.
85+
- **A reprocess reverts symbolicated frames.** The first pass overwrites `data` with the
86+
original-source position; the second pass reads that as if it were minified, and resets
87+
`trace-sym` to the minified trace. Any late map upload triggers this.
88+
89+
Change: cherry-pick `6b99a7a` onto the branch. It applies cleanly.
90+
91+
### W2 — keep the SDK's summary and signatures when there is no `crash.json` (blocker)
92+
93+
Hang (AppHang) reports and Electron renderer-gone reports ship without `crash.json`.
94+
`jobs/bundle.py:884-891` fills any non-bug report without one with signature `no-crash-file` and
95+
summary `<missing crash details>`. That overwrites the SDK's own summary ("Main thread hang
96+
detected") and signatures (`AppHang::Fair|Medium|Severe`), and because the appserver merges on
97+
`signatures: {$in}`, every hang and every renderer crash in an app collapses into one issue.
98+
99+
Change: when the environment is a JavaScript one (`environment.sdk.type == 'javascript'`) and there
100+
is no crash file, leave the summary and signatures as the SDK reported them. Other SDKs keep the
101+
current fallback. The SDK track separately adds a `crash.json` to both report kinds; this change
102+
makes the worker correct either way.
103+
104+
### W3 — respect the SDK's `user` frame flag (major)
105+
106+
`crash/javascript.py:445` sets `frame['user'] = True` for every frame. The SDK marks runtime frames
107+
(`node:`) and its own `@bugsee/*` frames as not the app's (`packages/core/src/crash.ts:178-186`), so
108+
issues currently group on a `@bugsee/capture` or `node:` frame instead of the app's call site.
109+
110+
Change: keep the SDK's value and default to `True` only when the flag is absent.
111+
112+
### W4 — no "at at" in titles (minor)
113+
114+
The summary format (`crash/javascript.py:461`, `"%s at %s"`) prefixes `at` to a location that,
115+
when unsymbolicated, is the SDK trace and already starts with `at `. Strip a leading `at ` from the
116+
location before formatting.
117+
118+
## viewer
119+
120+
### V1 — open the JavaScript app type in production (blocker)
121+
122+
`elements-application-create-dialog.component.ts:199` sets
123+
`isJavascriptTypeAvailable = config.getValue("env.type") !== "production"`, and line 200 reuses it
124+
for Rust. Only the create-dialog radio is gated.
125+
126+
Change: make JavaScript always available. Give Rust its own gate through
127+
`core/utils/deployment.ts isPreProductionFeatureAvailable()` — the helper that file names as the
128+
only definition — so opening JavaScript does not make Rust public too.
129+
130+
### V2 — debug data for JavaScript apps (major)
131+
132+
"Setup auto upload" branches only on unity, xamarin/dotnet, android and ios, so for a JS app it opens
133+
with zero steps. The upload dialog says "Upload mapping" and asks for a zip of debug information.
134+
135+
Change: add a JavaScript branch to the setup dialog that shows the `@bugsee/vite-plugin` and
136+
`@bugsee/webpack-plugin` configuration (and the `bugsee-cli` command for other bundlers), and use
137+
source-map wording in the upload dialog for JS apps. The iOS-only drop zone stays as it is; manual
138+
`.map` upload is not part of the beta flow.
139+
140+
### V3 — stop downloading `profile.json` to discard it (major)
141+
142+
`profile` is not in `recording-session.service.ts`'s ignored-types list, so a CPU profile (often
143+
several MB) is fetched and then dropped by `recording-helper.service.ts`'s `default:` branch. Add it
144+
to the ignored list. A flame-graph view comes after the beta.
145+
146+
### V4 — documentation links for JavaScript apps (major)
147+
148+
`getDocumentationDir.ts:5-6` returns the subtype, so the console hint links to
149+
`docs.bugsee.com/sdk/express/...`. Map every JavaScript subtype to `javascript`. The JavaScript
150+
section of the docs site does not exist yet; that is docs-repo work, tracked below.
151+
152+
### V5 — small fixes (minor)
153+
154+
- The app-list filter has no JavaScript option.
155+
- The issue list's reporter-email tooltip has text for iOS and Android only.
156+
- The application plate prints the raw `application.type`.
157+
- The performance tab computes `end - start` for spans that the SDK may leave without an end, which
158+
gives NaN widths; treat a missing end as the transaction end.
159+
- Onboarding install lines name only the adapter (`npm install @bugsee/react`) while the snippet
160+
imports `@bugsee/bugsee`, which resolves only through hoisting. Install both, or import from the
161+
adapter where it re-exports — whichever the adapter's actual exports support.
162+
- A native frame in a JavaScript app shows "missing debug information… upload". Per D2, Electron's
163+
own frames cannot be symbolicated during the beta, so the hint for those frames says that instead.
164+
165+
## Testing
166+
167+
Each change is test-first in its repo's own harness, with the mutator loop from
168+
`docs/implementation-standards.md` applied to every changed function:
169+
170+
- appserver: mocha (`NODE_ENV=test npx mocha`), plus `npm run eslint` and the circular-dependency
171+
check.
172+
- worker: `unittest` under `.venv314`, `PYTHONDONTWRITEBYTECODE=1` during mutation runs (stale
173+
`.pyc` files otherwise mask a mutation).
174+
- viewer: the specs that exist for the touched services, plus `npm run build`, which is the viewer's
175+
real gate.
176+
177+
Each PR then gets the convergent multi-agent review before it is marked ready.
178+
179+
## Rollout
180+
181+
Merging to `main` deploys each repo to staging only. After staging is checked with a real JS app
182+
(one browser session, one Node crash with a map, one mobile-browser session):
183+
184+
- appserver: the manual production workflow run. Production is 34 commits behind `main`, which
185+
also brings the `js` build format there — without it, bundler-plugin build registration fails in
186+
production today.
187+
- worker: a PR from `main` into `release` labelled `deploy-production`.
188+
- viewer: the manual production workflow run. This is the launch, since it opens the app type.
189+
190+
Order: worker and appserver first, viewer last, so the app type becomes creatable only once the
191+
backend accepts what it produces.
192+
193+
## SDK release track (not in these PRs)
194+
195+
Found by the same audit; each is a change in this repo:
196+
197+
- Every package is `private: true` and `.changeset/config.json` has `access: restricted`; nothing is
198+
on npm, so every onboarding snippet fails at `npm install`.
199+
- The reported version is hardcoded as `'0.1.0'` in six places (browser, node, webview, webworker,
200+
vercel-edge launch files and `core/src/carrier.ts`), so bumping `package.json` does not change it.
201+
- Hang reports (`node/src/hang-detection-provider.ts`) and Electron renderer-gone reports
202+
(`electron/src/renderer-incident-provider.ts`) send no `crash.json`. A renderer minidump is
203+
attached but never stackwalked.
204+
- The browser transport forwards a `user-agent` header, which is not in production's CORS
205+
allowed-headers list; Firefox is expected to fail the preflight.
206+
- An unrecognised OS is sent as `platform.type: ''`, which the session endpoint rejects as missing
207+
(permanent); send `'unknown'`.
208+
- Electron video is WebM, but the manifest carries no codec/format attributes, so the worker and
209+
viewer label it `video/mp4`.
210+
- A cross-origin "Script error." with no filename sends zero frames, so each occurrence is its own
211+
issue.
212+
- The performance uploader sends no `x-bugsee-internal` header and ignores `{ok:false}`.
213+
214+
Docs: the docs site has no JavaScript section, which V4 and the first-issue email both link to.
215+
216+
## Deferred
217+
218+
- Electron runtime system symbols (D2): an upload prefix routed to `electron_symbols.system` in the
219+
worker, plus a way to fetch Electron's published `.sym` files per version.
220+
- A CPU-profile view in the viewer.
221+
- Public resource links and integrations (Slack/Jira) that include the web replay; the public API's
222+
crash formatter, which currently renders JS crashes through the Android one; JS app-level
223+
"SDK outdated" flags, which cannot use a single version for an app with several runtimes.
224+
- Retention and rollups for performance data.

0 commit comments

Comments
 (0)