Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 41 additions & 0 deletions docs/design/evidence/byo-hosted-offer-2026-10-24/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
# Stalled BYO hosted bar

TASK-482/483, 2026-10-24. Real isolated Chromium with synthetic API responses
and the literal `fixture-only-key`. No real user session, credential, or
production write is used.

Lily's 16:28Z ruling supersedes the earlier in-flow placement: the offer is a
sticky bottom bar with an ink action, a top border and no shadow. Its 96px desktop
or 128px phone height is reserved from the first render; a negative top margin
means inserting the bar adds no flow height. The initial content pane padding
includes that same height, so full scroll reveals the final control above it.

Each language (`en`, `zh-CN`) and viewport (`1440×1000`, `390×844`) has:

- `offer-*.png`: the bar visible immediately at the initial scroll position.
- `full-scroll-*.png`: the last existing control remains above the bar.
- `copied-*.png`: command copy dismissed the offer.
- `reload-*.png`: same-tab reload, reissue/render the same agent's command,
advance past 30 seconds; the offer remains absent.
- `result-*.json`: bar/button dimensions, scroll/client widths, fixed event
names and browser errors. Phone captures advance the browser clock; desktop
captures use top-edge exit intent.

`command-tops-*.json` and `sticky-clearance-*.json` come from
`e2e/byo-offer-layout.spec.ts`. At 1440×1000 and 390×844, all four commands,
the memory textarea, final CTA row and both final buttons retain identical tops
when the bar appears. Memory and the final buttons are fully on screen while
the timer fires. The bar stays in view at the top and bottom of the pane; at
full scroll the last button clears its top edge. Reserved height equals measured
bar height both before key issuance and after the bar appears.

Run against the normal frontend dev server from the repository root:
`E2E_BASE_URL=http://localhost:3000 npx playwright test e2e/byo-offer-layout.spec.ts`.
All API responses and authentication are fixtures; no backend is required.

Chinese action: `改为在这里运行`.
Chinese supporting text: `无需终端。在这个 Pod 中启动一个新的托管智能体。`

### Provision error at 390 px

`provision-error-en-390.png` shows the intercepted 503 response with a two-line error. The sticky bar uses a minimum height and grows for error text; it has no nested scroller. The Chromium spec reproduced clipping before the CSS fix and passes after it, including the existing stationary controls, viewport and full-scroll clearance assertions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
{
"width": 1440,
"before": [
-595,
-258,
-61,
77
],
"after": [
-595,
-258,
-61,
77
],
"controlsBefore": [
512,
703,
703,
786
],
"controlsAfter": [
512,
703,
703,
786
]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
{
"width": 390,
"before": [
-1168,
-591,
-314,
-136
],
"after": [
-1168,
-591,
-314,
-136
],
"controlsBefore": [
399,
621,
621,
724
],
"controlsAfter": [
399,
621,
621,
724
]
}
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
{
"lang": "en",
"width": 1440,
"metrics": {
"buttonHeight": 40,
"buttonWidth": 160.09375,
"rowWidth": 1240,
"barHeight": 96,
"position": "sticky",
"bg": "rgb(16, 24, 40)",
"afterCommand": true,
"scrollWidth": 1440,
"clientWidth": 1440
},
"copySuppressed": true,
"reloadSuppressed": true,
"phoneTimerOnly": false,
"events": [
{
"step": "page-viewed"
},
{
"step": "seat-created"
},
{
"step": "cli-command-copied"
},
{
"step": "page-viewed"
},
{
"step": "seat-created"
}
],
"errors": []
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
{
"lang": "en",
"width": 390,
"metrics": {
"buttonHeight": 44,
"buttonWidth": 290,
"rowWidth": 322,
"barHeight": 128,
"position": "sticky",
"bg": "rgb(16, 24, 40)",
"afterCommand": true,
"scrollWidth": 390,
"clientWidth": 390
},
"copySuppressed": true,
"reloadSuppressed": true,
"phoneTimerOnly": true,
"events": [
{
"step": "page-viewed"
},
{
"step": "seat-created"
},
{
"step": "cli-command-copied"
},
{
"step": "page-viewed"
},
{
"step": "seat-created"
}
],
"errors": []
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
{
"lang": "zh-CN",
"width": 1440,
"metrics": {
"buttonHeight": 40,
"buttonWidth": 133.96875,
"rowWidth": 1240,
"barHeight": 96,
"position": "sticky",
"bg": "rgb(16, 24, 40)",
"afterCommand": true,
"scrollWidth": 1440,
"clientWidth": 1440
},
"copySuppressed": true,
"reloadSuppressed": true,
"phoneTimerOnly": false,
"events": [
{
"step": "page-viewed"
},
{
"step": "seat-created"
},
{
"step": "cli-command-copied"
},
{
"step": "page-viewed"
},
{
"step": "seat-created"
}
],
"errors": []
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
{
"lang": "zh-CN",
"width": 390,
"metrics": {
"buttonHeight": 44,
"buttonWidth": 290,
"rowWidth": 322,
"barHeight": 128,
"position": "sticky",
"bg": "rgb(16, 24, 40)",
"afterCommand": true,
"scrollWidth": 390,
"clientWidth": 390
},
"copySuppressed": true,
"reloadSuppressed": true,
"phoneTimerOnly": true,
"events": [
{
"step": "page-viewed"
},
{
"step": "seat-created"
},
{
"step": "cli-command-copied"
},
{
"step": "page-viewed"
},
{
"step": "seat-created"
}
],
"errors": []
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"width": 1440,
"barHeight": 96,
"reservedHeight": 96,
"barTop": 889,
"lastControlBottom": 825
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"width": 390,
"barHeight": 128,
"reservedHeight": 128,
"barTop": 716,
"lastControlBottom": 652
}
39 changes: 39 additions & 0 deletions docs/development/byo-funnel-telemetry.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
# BYO funnel events

The authenticated BYO page posts fixed event names to `POST /api/stats/byo-step`.
The endpoint stores `userId`, `step`, and server time in `byo_telemetry`.
Commands, keys, names, and clipboard contents are never sent.

As of TASK-483 (2026-10-24), the browser sends:

| Event | Observed state |
| --- | --- |
| `page-viewed` | An authenticated user mounted the BYO page, including its hosted entry variant. Once per mount, including React StrictMode effect replay. |
| `seat-created` | A runtime key was issued and its command rendered. Once per issued command. This is the command-exposure denominator. |
| `mcp-command-copied` | The Claude or Cursor copy button successfully wrote the command. |
| `cli-command-copied` | The listener copy button successfully wrote the command. |
| `token-copied` | The token copy button successfully wrote the key. |
| `listen-confirmed` | The connection poll observed this agent using its key after issuance (with existing clock-skew allowance). |
| `listen-timeout` | The page's connection polling window expired. |

Before TASK-483, the client never emitted `page-viewed` or `seat-created`.
Historical counts of those events cannot measure page or command exposure.
A selected-text copy from a command or key block dismisses the hosted offer but does not claim that a complete
command was copied in telemetry. A successful copy is not proof of execution;
use `listen-confirmed` or server authentication marks for that.

TASK-482 offers a new hosted agent in the same pod after 30 seconds with an
issued command and no copy, or desktop (over 760px) exit through the top edge. At 390px only the timer triggers it. The offer is a sticky bottom bar with an ink action, a top border and a status announcement. Its height is reserved from the first render, and its negative margin adds no flow height when it appears. It is shown once per owner/agent/pod panel per browser-tab session; a prior display or copy suppresses it after reload. Only a non-secret seen marker is stored in sessionStorage. Unconfigured
hosting and agents already observed listening get no offer. Existing BYO identity
and credentials are retained. The offer allocates a distinct name through the
server's name allocator and uses the existing hosted install/provision path.
Hosting errors preserve the command and expose a retry beside the offer. The allocated hosted name is retained for the issued key, so a provision failure retries the existing installation instead of allocating another cap slot.

The placement invariant is checked in real Chromium by
`e2e/byo-offer-layout.spec.ts`: with all command blocks expanded, each block's
viewport top must be identical before and after the offer appears. The memory
textarea, final action row and both action buttons must also stay stationary,
with those controls fully in the viewport while the timer fires. The bar must
remain visible at both scroll extremes and must not cover the last control at
full scroll; the reserved height must equal its measured height. The DOM order
is also pinned in the BYO render and v2 layout-invariant Jest tests.
Loading
Loading