From a3263ea5163469cca74a60ef359a042f57b61bbc Mon Sep 17 00:00:00 2001 From: Lily Shen <115414357+lilyshen0722@users.noreply.github.com> Date: Tue, 27 Oct 2026 15:49:41 -0700 Subject: [PATCH 1/5] feat(onboarding): record the OS on the first Bring your own open The BYO page passes the OS it detected (?os=mac|linux|windows|other) on its load request, and markByoOpened writes it as onboarding.byoOpenedOs in the same write-once update as byoOpenedAt. Any other value records the visit without an OS. The listener only runs on a Mac today, so this splits "looked and left" by whether setup could have worked. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01MDs9DRK3nqxjeAV7xL8uhg --- backend/__tests__/unit/routes/hosted.test.js | 7 +++- .../unit/services/onboardingMarks.test.js | 22 ++++++++++ backend/models/User.ts | 3 ++ backend/routes/hosted.ts | 5 ++- backend/services/onboardingMarks.ts | 24 ++++++++++- .../v2/__tests__/V2AgentBYOHosted.test.tsx | 42 ++++++++++++++++++- frontend/src/v2/components/V2AgentBYO.tsx | 17 +++++++- 7 files changed, 112 insertions(+), 8 deletions(-) diff --git a/backend/__tests__/unit/routes/hosted.test.js b/backend/__tests__/unit/routes/hosted.test.js index 7b83e74ea7..68e3c05fdc 100644 --- a/backend/__tests__/unit/routes/hosted.test.js +++ b/backend/__tests__/unit/routes/hosted.test.js @@ -183,7 +183,12 @@ describe('/api/hosted', () => { const res = await request(app).get('/api/hosted/availability'); expect(res.status).toBe(200); expect(mockMarkByoOpened).toHaveBeenCalledTimes(1); - expect(mockMarkByoOpened).toHaveBeenCalledWith('owner-1'); + expect(mockMarkByoOpened).toHaveBeenCalledWith('owner-1', undefined); + }); + + it('passes the OS the page detected to the first-visit mark', async () => { + await request(app).get('/api/hosted/availability?os=linux'); + expect(mockMarkByoOpened).toHaveBeenCalledWith('owner-1', 'linux'); }); it('checks new-pod hosted availability without marking Bring your own opened', async () => { diff --git a/backend/__tests__/unit/services/onboardingMarks.test.js b/backend/__tests__/unit/services/onboardingMarks.test.js index 1cc7f57bd9..0922a58382 100644 --- a/backend/__tests__/unit/services/onboardingMarks.test.js +++ b/backend/__tests__/unit/services/onboardingMarks.test.js @@ -139,6 +139,28 @@ describe('markByoOpened', () => { expect(update.$set['onboarding.byoOpenedAt']).toBeInstanceOf(Date); }); + test('records the OS the page detected in the same write-once update', () => { + markByoOpened('u1', 'windows'); + const [filter, update] = mockUpdateOne.mock.calls[0]; + expect(filter).toEqual({ _id: 'u1', isBot: { $ne: true }, 'onboarding.byoOpenedAt': { $exists: false } }); + expect(Object.keys(update.$set)).toEqual(['onboarding.byoOpenedAt', 'onboarding.byoOpenedOs']); + expect(update.$set['onboarding.byoOpenedOs']).toBe('windows'); + }); + + test.each(['mac', 'linux', 'windows', 'other'])('keeps os=%s', (os) => { + markByoOpened('u1', os); + expect(mockUpdateOne.mock.calls[0][1].$set['onboarding.byoOpenedOs']).toBe(os); + }); + + test.each([['ios'], ['Mac'], [''], [['mac']], [{ os: 'mac' }], [42]])( + 'any other os value (%p) records the visit without an OS', + (os) => { + markByoOpened('u1', os); + expect(mockUpdateOne).toHaveBeenCalledTimes(1); + expect(Object.keys(mockUpdateOne.mock.calls[0][1].$set)).toEqual(['onboarding.byoOpenedAt']); + }, + ); + test('no person, no write', () => { markByoOpened(''); markByoOpened(undefined); diff --git a/backend/models/User.ts b/backend/models/User.ts index 3be3e3ba82..1e4b8534f9 100644 --- a/backend/models/User.ts +++ b/backend/models/User.ts @@ -210,6 +210,8 @@ export interface IUser extends Document { firstStartId?: string; firstStartAt?: Date; byoOpenedAt?: Date; + /** OS the page detected on that first open, written with byoOpenedAt. */ + byoOpenedOs?: 'mac' | 'linux' | 'windows' | 'other'; /** First BYO setup command copied in the browser, never overwritten. */ byoCommandCopiedAt?: Date; /** Fixed kind only; command contents and tokens are never stored. */ @@ -460,6 +462,7 @@ const userSchema = new Schema({ firstStartId: { type: String }, firstStartAt: { type: Date }, byoOpenedAt: { type: Date }, + byoOpenedOs: { type: String, enum: ['mac', 'linux', 'windows', 'other'] }, byoCommandCopiedAt: { type: Date }, byoCommandType: { type: String, enum: ['mcp', 'cli'] }, byoFirstConnectionAttemptAt: { type: Date }, diff --git a/backend/routes/hosted.ts b/backend/routes/hosted.ts index e36b1163a5..fe1775f8e2 100644 --- a/backend/routes/hosted.ts +++ b/backend/routes/hosted.ts @@ -191,10 +191,11 @@ const relayWorkerError = (res: express.Response, error: any) => { * * Only the Bring your own page calls this, on load, so it also records the * person's first visit to that page (write-once, fire-and-forget; - * services/onboardingMarks.ts). + * services/onboardingMarks.ts). `?os=` is the OS the page detected; the mark + * keeps only mac, linux, windows or other and records no OS for anything else. */ router.get('/availability', hostedRateLimit, auth, async (req: any, res: any) => { - markByoOpened(getUserId(req)); + markByoOpened(getUserId(req), req.query?.os); const engine = engineForNewHostedInstall(); // The caller's own plan line (TASK-268), so the page states the limit they get. const caps = await hostedRuntime.hostedCapsForOwner(getUserId(req)); diff --git a/backend/services/onboardingMarks.ts b/backend/services/onboardingMarks.ts index 81b0f739a4..dae63cb5a3 100644 --- a/backend/services/onboardingMarks.ts +++ b/backend/services/onboardingMarks.ts @@ -25,6 +25,11 @@ * read from the page's own load request (GET /api/hosted/availability, which * nothing else calls). It tells "never looked at setup" apart from "looked and * left". + * + * byoOpenedOs is written with it, in the same write-once update: the OS the + * page detected from the browser, one of BYO_OPENED_OS. The listener only runs + * on a Mac today, so this splits "left" by whether setup could have worked at + * all. An unknown or missing value records the visit without an OS. */ import User from '../models/User'; import { SCOUT_STARTS } from '../config/native-agents/scout'; @@ -92,15 +97,30 @@ function recordFirstStart(id: string, startId: string): void { } } -export function markByoOpened(userId: unknown): void { +export const BYO_OPENED_OS = ['mac', 'linux', 'windows', 'other'] as const; +export type ByoOpenedOs = typeof BYO_OPENED_OS[number]; + +export const parseByoOpenedOs = (value: unknown): ByoOpenedOs | undefined => ( + typeof value === 'string' && (BYO_OPENED_OS as readonly string[]).includes(value) + ? value as ByoOpenedOs + : undefined +); + +export function markByoOpened(userId: unknown, os?: unknown): void { const id = String(userId || ''); if (!id) return; if (byoOpenedMarked.has(id)) return; byoOpenedMarked.add(id); + const detectedOs = parseByoOpenedOs(os); try { User.updateOne( { _id: id, isBot: { $ne: true }, 'onboarding.byoOpenedAt': { $exists: false } }, - { $set: { 'onboarding.byoOpenedAt': new Date() } }, + { + $set: { + 'onboarding.byoOpenedAt': new Date(), + ...(detectedOs ? { 'onboarding.byoOpenedOs': detectedOs } : {}), + }, + }, ).catch(() => { byoOpenedMarked.delete(id); }); diff --git a/frontend/src/v2/__tests__/V2AgentBYOHosted.test.tsx b/frontend/src/v2/__tests__/V2AgentBYOHosted.test.tsx index 45220a6f4d..d1bebcaa04 100644 --- a/frontend/src/v2/__tests__/V2AgentBYOHosted.test.tsx +++ b/frontend/src/v2/__tests__/V2AgentBYOHosted.test.tsx @@ -176,7 +176,7 @@ describe('V2AgentBYO — Run it here', () => { ); await waitFor(() => expect(screen.getByTestId('byo-mode-hosted')).toBeInTheDocument()); // let the availability effect settle, then assert it did not stomp the mode - await waitFor(() => expect(axios.get.mock.calls.some(([url]) => url === '/api/hosted/availability')).toBe(true)); + await waitFor(() => expect(axios.get.mock.calls.some(([url]) => url.startsWith('/api/hosted/availability'))).toBe(true)); await waitFor(() => expect(screen.getByTestId('byo-mode-byo')).toHaveAttribute('aria-pressed', 'true')); expect(screen.getByTestId('byo-mode-hosted')).toHaveAttribute('aria-pressed', 'false'); window.history.pushState({}, '', '/'); @@ -447,3 +447,43 @@ describe('V2AgentBYO — Run it here', () => { expect(axios.post.mock.calls[0][1]).toMatchObject({ config: { runtime: { runtimeType: 'webhook' } } }); }); }); + +// The page's load request carries the OS it detected, for the server's +// first-open mark (User.onboarding.byoOpenedOs). Unlike the setup tabs, an +// OS it can't place is reported as 'other', never as a Mac. +describe('V2AgentBYO — detected OS on the availability request', () => { + const availabilityUrl = () => axios.get.mock.calls + .map(([url]) => url) + .find((url) => url.startsWith('/api/hosted/availability')); + + const withPlatform = async (platform, userAgent) => { + const uaData = Object.getOwnPropertyDescriptor(window.navigator, 'userAgentData'); + const ua = jest.spyOn(window.navigator, 'userAgent', 'get'); + if (userAgent) ua.mockReturnValue(userAgent); + Object.defineProperty(window.navigator, 'userAgentData', { + value: platform === undefined ? undefined : { platform }, configurable: true, + }); + try { + mockGet({ configured: true }); + renderPage(); + await waitFor(() => expect(availabilityUrl()).toBeDefined()); + return availabilityUrl(); + } finally { + ua.mockRestore(); + if (uaData) Object.defineProperty(window.navigator, 'userAgentData', uaData); + else delete window.navigator.userAgentData; + } + }; + + test.each([ + ['macOS', undefined, 'mac'], + ['Windows', undefined, 'windows'], + ['Linux', undefined, 'linux'], + ['Android', undefined, 'other'], + [undefined, 'Mozilla/5.0 (iPhone; CPU iPhone OS 17_0 like Mac OS X)', 'other'], + [undefined, 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7)', 'mac'], + [undefined, 'Mozilla/5.0 (linux) AppleWebKit/537.36 (KHTML, like Gecko) jsdom/20.0.3', 'other'], + ])('platform %p / UA %p reports os=%s', async (platform, userAgent, os) => { + expect(await withPlatform(platform, userAgent)).toBe(`/api/hosted/availability?os=${os}`); + }); +}); diff --git a/frontend/src/v2/components/V2AgentBYO.tsx b/frontend/src/v2/components/V2AgentBYO.tsx index 74c341b0f3..a70bb6327e 100644 --- a/frontend/src/v2/components/V2AgentBYO.tsx +++ b/frontend/src/v2/components/V2AgentBYO.tsx @@ -90,6 +90,18 @@ const browserOs = (): SetupOs => { if (/X11|CrOS|Linux (x86_64|aarch64|armv|i686)|^Linux$/.test(platform) && !/Android/.test(platform)) return 'linux'; return 'mac'; }; +// What the page saw, for the first-open mark only (byoOpenedOs). Unlike +// browserOs it does not default to mac: a browser it can't place is 'other'. +type OpenedOs = SetupOs | 'other'; +const openedOs = (): OpenedOs => { + if (typeof navigator === 'undefined') return 'other'; + const nav = navigator as Navigator & { userAgentData?: { platform?: string } }; + const platform = nav.userAgentData?.platform || nav.userAgent || ''; + if (/iPhone|iPad|Android/.test(platform)) return 'other'; + if (/Mac|macOS/.test(platform)) return 'mac'; + const os = browserOs(); + return os === 'mac' ? 'other' : os; +}; const SETUP_OS_OPTIONS: Array<{ os: SetupOs; labelKey: string }> = [ { os: 'mac', labelKey: 'agentByo.listen.osMac' }, { os: 'linux', labelKey: 'agentByo.listen.osLinux' }, @@ -492,8 +504,9 @@ const V2AgentBYO: React.FC = () => { } try { // This request also records the person's first visit to this page on - // the server (User.onboarding.byoOpenedAt). Keep it on page load. - const availability = await api.get('/api/hosted/availability'); + // the server (User.onboarding.byoOpenedAt, and byoOpenedOs from ?os). + // Keep it on page load. + const availability = await api.get(`/api/hosted/availability?os=${openedOs()}`); if (cancelled) return; if (availability && typeof availability.configured === 'boolean') { // Sets no mode. This effect re-runs on every pod change (podId is a From f311e37ead22d660b9bc3968bb0ce4e3a2ef88b7 Mon Sep 17 00:00:00 2001 From: Lily Shen <115414357+lilyshen0722@users.noreply.github.com> Date: Tue, 27 Oct 2026 16:52:27 -0700 Subject: [PATCH 2/5] fix(onboarding): an iPad in desktop mode is 'other'; round-trip byoOpenedOs CodeRabbit on #2903: iPadOS Safari in desktop mode sends a Mac user agent, so a Mac-like platform with touch points is now 'other'. Adds a service-tier test that byoOpenedOs round-trips through the real User schema, an unknown value stores no OS, and a later open never overwrites the first. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01MDs9DRK3nqxjeAV7xL8uhg --- .../service/onboardingByoOpenedOs.test.js | 59 +++++++++++++++++++ .../v2/__tests__/V2AgentBYOHosted.test.tsx | 11 +++- frontend/src/v2/components/V2AgentBYO.tsx | 3 +- 3 files changed, 71 insertions(+), 2 deletions(-) create mode 100644 backend/__tests__/service/onboardingByoOpenedOs.test.js diff --git a/backend/__tests__/service/onboardingByoOpenedOs.test.js b/backend/__tests__/service/onboardingByoOpenedOs.test.js new file mode 100644 index 0000000000..29618e2b3d --- /dev/null +++ b/backend/__tests__/service/onboardingByoOpenedOs.test.js @@ -0,0 +1,59 @@ +/** + * byoOpenedOs round-trips through the real User schema: the enum admits the + * four values markByoOpened writes, and the mark's write-once filter keeps the + * first open's OS. The unit test (unit/services/onboardingMarks.test.js) pins + * the update payload; this pins that the schema stores it. + */ +const User = require('../../models/User'); +const { markByoOpened, resetOnboardingMarksForTest } = require('../../services/onboardingMarks'); +const { setupMongoDb, closeMongoDb, clearMongoDb } = require('../utils/testUtils'); + +const makeUser = (name) => User.create({ + username: name, email: `${name}@example.test`, password: 'Opened-os-123!', +}); + +const settle = async (id, predicate) => { + for (let i = 0; i < 50; i += 1) { + // eslint-disable-next-line no-await-in-loop + const row = await User.findById(id).lean(); + if (predicate(row)) return row; + // eslint-disable-next-line no-await-in-loop + await new Promise((r) => setTimeout(r, 20)); + } + return User.findById(id).lean(); +}; + +beforeAll(setupMongoDb); +afterAll(closeMongoDb); +beforeEach(async () => { + await clearMongoDb(); + resetOnboardingMarksForTest(); +}); + +test.each(['mac', 'linux', 'windows', 'other'])('stores os=%s with the first-open time', async (os) => { + const user = await makeUser(`opened-${os}`); + markByoOpened(String(user._id), os); + const row = await settle(user._id, (r) => r?.onboarding?.byoOpenedAt); + expect(row.onboarding.byoOpenedAt).toBeInstanceOf(Date); + expect(row.onboarding.byoOpenedOs).toBe(os); +}); + +test('an unknown value records the visit without an OS', async () => { + const user = await makeUser('opened-ios'); + markByoOpened(String(user._id), 'ios'); + const row = await settle(user._id, (r) => r?.onboarding?.byoOpenedAt); + expect(row.onboarding.byoOpenedAt).toBeInstanceOf(Date); + expect(row.onboarding).not.toHaveProperty('byoOpenedOs'); +}); + +test('a later open from another OS does not overwrite the first', async () => { + const user = await makeUser('opened-twice'); + markByoOpened(String(user._id), 'windows'); + const first = await settle(user._id, (r) => r?.onboarding?.byoOpenedAt); + resetOnboardingMarksForTest(); // as after a restart + markByoOpened(String(user._id), 'mac'); + await new Promise((r) => setTimeout(r, 200)); + const row = await User.findById(user._id).lean(); + expect(row.onboarding.byoOpenedOs).toBe('windows'); + expect(row.onboarding.byoOpenedAt).toEqual(first.onboarding.byoOpenedAt); +}); diff --git a/frontend/src/v2/__tests__/V2AgentBYOHosted.test.tsx b/frontend/src/v2/__tests__/V2AgentBYOHosted.test.tsx index d1bebcaa04..13a16ff2d8 100644 --- a/frontend/src/v2/__tests__/V2AgentBYOHosted.test.tsx +++ b/frontend/src/v2/__tests__/V2AgentBYOHosted.test.tsx @@ -456,10 +456,12 @@ describe('V2AgentBYO — detected OS on the availability request', () => { .map(([url]) => url) .find((url) => url.startsWith('/api/hosted/availability')); - const withPlatform = async (platform, userAgent) => { + const withPlatform = async (platform, userAgent, touchPoints = 0) => { const uaData = Object.getOwnPropertyDescriptor(window.navigator, 'userAgentData'); const ua = jest.spyOn(window.navigator, 'userAgent', 'get'); if (userAgent) ua.mockReturnValue(userAgent); + const touch = Object.getOwnPropertyDescriptor(window.navigator, 'maxTouchPoints'); + Object.defineProperty(window.navigator, 'maxTouchPoints', { value: touchPoints, configurable: true }); Object.defineProperty(window.navigator, 'userAgentData', { value: platform === undefined ? undefined : { platform }, configurable: true, }); @@ -470,6 +472,8 @@ describe('V2AgentBYO — detected OS on the availability request', () => { return availabilityUrl(); } finally { ua.mockRestore(); + if (touch) Object.defineProperty(window.navigator, 'maxTouchPoints', touch); + else delete window.navigator.maxTouchPoints; if (uaData) Object.defineProperty(window.navigator, 'userAgentData', uaData); else delete window.navigator.userAgentData; } @@ -486,4 +490,9 @@ describe('V2AgentBYO — detected OS on the availability request', () => { ])('platform %p / UA %p reports os=%s', async (platform, userAgent, os) => { expect(await withPlatform(platform, userAgent)).toBe(`/api/hosted/availability?os=${os}`); }); + + test('an iPad in desktop mode (Mac UA, touch) reports os=other', async () => { + expect(await withPlatform(undefined, 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7)', 5)) + .toBe('/api/hosted/availability?os=other'); + }); }); diff --git a/frontend/src/v2/components/V2AgentBYO.tsx b/frontend/src/v2/components/V2AgentBYO.tsx index a70bb6327e..f5f3d2443b 100644 --- a/frontend/src/v2/components/V2AgentBYO.tsx +++ b/frontend/src/v2/components/V2AgentBYO.tsx @@ -98,7 +98,8 @@ const openedOs = (): OpenedOs => { const nav = navigator as Navigator & { userAgentData?: { platform?: string } }; const platform = nav.userAgentData?.platform || nav.userAgent || ''; if (/iPhone|iPad|Android/.test(platform)) return 'other'; - if (/Mac|macOS/.test(platform)) return 'mac'; + // iPadOS Safari in desktop mode reports a Mac user agent; touch gives it away. + if (/Mac|macOS/.test(platform)) return (nav.maxTouchPoints || 0) > 1 ? 'other' : 'mac'; const os = browserOs(); return os === 'mac' ? 'other' : os; }; From 424e490e621188553fee7ceebe31bd7c5e6a3972 Mon Sep 17 00:00:00 2001 From: Lily Shen <115414357+lilyshen0722@users.noreply.github.com> Date: Sat, 31 Oct 2026 03:41:54 -0700 Subject: [PATCH 3/5] docs(onboarding): name Mac as the only listener OS (#2903) The listener runs only on Mac: listenerRunsOn in V2AgentBYO.tsx is os === 'mac'. The comment said Mac and Linux, which described the wrong gate and would mislead the reader of byoOpenedOs. Co-Authored-By: Claude Haiku 5.5 Co-authored-by: dart Agent-Model: claude/claude-haiku-5-5/default --- backend/services/onboardingMarks.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/backend/services/onboardingMarks.ts b/backend/services/onboardingMarks.ts index f9f8ce5ea2..dde3d197a7 100644 --- a/backend/services/onboardingMarks.ts +++ b/backend/services/onboardingMarks.ts @@ -28,8 +28,9 @@ * * byoOpenedOs is written with it, in the same write-once update: the OS the * page detected from the browser, one of BYO_OPENED_OS. The listener runs only - * on Mac and Linux today, so this splits "left" by whether setup could have worked at - * all. An unknown or missing value records the visit without an OS. + * on Mac (listenerRunsOn in V2AgentBYO.tsx is os === 'mac'), so this splits + * "left" by whether setup could have worked at all. An unknown or missing value + * records the visit without an OS. * * Three surface marks come from the browser through POST /api/stats/byo-step, * as fixed step names (D139): inviteCardShownAt and inviteCardDismissedAt for From 5dc7729734074097a0dcf3d6139a5ecbf74d606f Mon Sep 17 00:00:00 2001 From: Lily Shen <115414357+lilyshen0722@users.noreply.github.com> Date: Sat, 31 Oct 2026 13:37:47 -0700 Subject: [PATCH 4/5] docs(onboarding): byoOpenedOs in the signup-stall runbook; the split is about the listener path (#2903) Co-Authored-By: Claude Opus 5.5 Co-authored-by: Ember Agent-Model: claude/claude-opus-5-5/medium --- backend/services/onboardingMarks.ts | 2 +- docs/runbooks/new-signup-stall.md | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/backend/services/onboardingMarks.ts b/backend/services/onboardingMarks.ts index f38c8d9f2e..a3e6d9236f 100644 --- a/backend/services/onboardingMarks.ts +++ b/backend/services/onboardingMarks.ts @@ -29,7 +29,7 @@ * byoOpenedOs is written with it, in the same write-once update: the OS the * page detected from the browser, one of BYO_OPENED_OS. The listener runs only * on Mac (listenerRunsOn in V2AgentBYO.tsx is os === 'mac'), so this splits - * "left" by whether setup could have worked at all. An unknown or missing value + * "left" by whether the listener path could have worked. An unknown or missing value * records the visit without an OS. * * Three surface marks come from the browser through POST /api/stats/byo-step, diff --git a/docs/runbooks/new-signup-stall.md b/docs/runbooks/new-signup-stall.md index 9b21116288..c71a62803e 100644 --- a/docs/runbooks/new-signup-stall.md +++ b/docs/runbooks/new-signup-stall.md @@ -25,6 +25,7 @@ confirm, for example against `lastActive` or whether they sent a message | `podOpenedAt` | the first authorized first-page fetch (no `before` cursor) of the messages of any pod they created; for a new signup that is normally My Workspace. It is set before the messages are read, so it records the request, not a render | their client asked for a pod's messages; for a new signup, very likely the welcome | | `firstStartAt` + `firstStartId` | their first message in a pod they created matches a start's label, or the server resolves a click on their own "Run an agent here" | they acted, and which choice | | `byoOpenedAt` | they first load Bring your own (`GET /api/hosted/availability`) | they looked at setup | +| `byoOpenedOs` | written with `byoOpenedAt`, from the `?os=` the page sends: `mac`, `linux`, `windows` or `other`. Unset for anyone who opened the page before it shipped, and for an unknown value | whether the listener path (Mac only) could have worked for them | Read them together with whether the person sent a message in their workspace, and when (timestamps and ids). From 3a1339aab1aae63bb979c45f05216566519995e8 Mon Sep 17 00:00:00 2001 From: Lily Shen <115414357+lilyshen0722@users.noreply.github.com> Date: Sat, 31 Oct 2026 13:38:00 -0700 Subject: [PATCH 5/5] docs(onboarding): re-wrap the byoOpenedOs comment Co-Authored-By: Claude Opus 5.5 Co-authored-by: Ember Agent-Model: claude/claude-opus-5-5/medium --- backend/services/onboardingMarks.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/backend/services/onboardingMarks.ts b/backend/services/onboardingMarks.ts index a3e6d9236f..62db8ffb75 100644 --- a/backend/services/onboardingMarks.ts +++ b/backend/services/onboardingMarks.ts @@ -29,8 +29,8 @@ * byoOpenedOs is written with it, in the same write-once update: the OS the * page detected from the browser, one of BYO_OPENED_OS. The listener runs only * on Mac (listenerRunsOn in V2AgentBYO.tsx is os === 'mac'), so this splits - * "left" by whether the listener path could have worked. An unknown or missing value - * records the visit without an OS. + * "left" by whether the listener path could have worked. An unknown or + * missing value records the visit without an OS. * * Three surface marks come from the browser through POST /api/stats/byo-step, * as fixed step names: inviteCardShownAt and inviteCardDismissedAt for