---
name: STAGESHARE_UPLINK
for: wellscope.stageshare/1.0.0
homepage: https://dx.horizontalwireline.ai/STAGESHARE_UPLINK.md
---

# Stage Share uplink — keep the overlay LIVE

The guest pill is **LIVE** only while the **Edge Pi / cabin** keeps writing to this room. EcoDrive telemetry does **not** count. Sitting on surface, standby, speed 0, or negative MD is still a live share — keep sending.

Canonical: `GET https://dx.horizontalwireline.ai/STAGESHARE_UPLINK.md`

Contract stays `wellscope.stageshare/1.0.0`. Privilege `stageshare`. Not `/v1/inbound`. Not partner `/v1/logs`.

## What the overlay watches

DX sets `last_handshake_at` when the **operator `hwx_` key** (the Pi) does any of:

| Write | When |
|-------|------|
| `POST /v1/stageshare/rooms/{id}/pointer` | **Heartbeat.** Send this on a timer for the whole live share. |
| Operator WS `pointer-move` | Same as HTTP pointer if the Pi already holds an operator `sst_` socket. |
| `PUT …/logs/{lid}/content` | Full LAS replace (live or frozen). |
| `POST …/logs/{lid}/append` | Live LAS tail (`X-Append-Offset` = current `bytes`). |
| `POST …/logs/{lid}/promote` | Snapshot live → frozen. Preview + measured `span` first — [`/STAGESHARE_LOGS.md`](https://dx.horizontalwireline.ai/STAGESHARE_LOGS.md). |
| `PUT …/survey` | Pad survey correction (create-time `survey` is enough once). |

After **2 minutes** with none of those, guests see **OFFLINE** and `Truck offline, for XX min`. The room stays `live`. Files stay open. The next Pi write restores the LIVE pill (`WS truck-online`).

`GET` anything, guest `sst_` cursor moves, chat, and EcoDrive `/v1/inbound` or `truck_telemetry` **do not** refresh the handshake.

## Required while the room is `live`

Send a pointer **at least every 30 seconds** (15 s is safer) from room create until finalize — including:

- On surface / out of hole (MD **−2**, **−30**, or any negative is valid)
- Standby, waiting on location, between runs
- Speed **0**, tension only changing a few pounds
- Recorder idle (no new LAS bytes)

Do **not** gate the timer on “in hole”, “LAS growing”, or “depth changed.” A one-shot LAS PUT at share start is **not** a heartbeat.

### Minimum heartbeat (HTTP)

```
POST /v1/stageshare/rooms/{id}/pointer
Authorization: Bearer hwx_…
WellScope-Contract: wellscope.stageshare/1.0.0
Content-Type: application/json
```

```
{
  "depth_ft": -2.0,
  "column_id": "live",
  "x_frac": 0.5,
  "tension_lbs": 670,
  "speed_ftpm": 0
}
```

- `depth_ft` — current tool **MD** (feet). Surface/standby may be negative. Do not invent 0 to “look in hole.”
- `column_id` — `live` while this is the current run (`tally` \| `live` \| `rih` \| `corr` \| `pump` \| `pooh`).
- `x_frac` — 0–1 across that column (cabin cursor). `0.5` is fine if you have no track position.
- `tension_lbs`, `speed_ftpm` — optional but paint the guest header. Speed is magnitude, never a signed direction.

204 empty body. Never send `css_px`, `x`, or `y`.

View-hint only also counts as a handshake (no gold tool line):

```
{ "tool_depth_ft": -2.0, "tension_lbs": 670, "speed_ftpm": 0 }
```

Prefer the full pointer so guests see the tool bubble.

### Live lead (`live_tail`) — CCLR ahead of the 10 ft LAS window

The recorder often waits **~10 ft** before appending LAS. The cabin already has samples between the last flushed row and the tool. Stage Share will not invent those — send them on the same pointer as `live_tail`.

Guest plot: committed LAS (CCLF ±0.75 V / CCLR / tension / speed) **plus** this lead on the live column. CCLR (violet) is on. The gold tool line and Follow target are Pi `depth_ft` / hello `view_hint.tool_depth_ft`. Gold Edge/cabin `pointer-move` cursors are other people — do **not** treat them as the tool.

```
{
  "depth_ft": 14.5,
  "column_id": "live",
  "x_frac": 0.5,
  "tension_lbs": 451.5,
  "speed_ftpm": 2.8,
  "live_tail": {
    "from_ft": 8.0,
    "to_ft": 14.5,
    "step_ft": 0.25,
    "dept_ft": [8.25, 8.50, 8.75],
    "cclr_v": [0.12, 0.18, -0.04],
    "cclf_v": [0.03, 0.04, 0.01],
    "tension_lbs": [450.1, 450.8, 451.2],
    "speed_ftpm": [2.1, 2.4, 2.8]
  }
}
```

Rules:

- **Only the uncommitted window.** Samples **after** the last depth already in the live LAS (strictly past the last flushed `DEPT`). Do not resend the whole file.
- Typical length is **one recorder step** (≤ ~10 ft). Cap **80** samples. DX drops the rest.
- Parallel arrays, same length as `dept_ft`. `cclr_v` is the one guests need for the lead; `cclf_v` / `tension_lbs` / `speed_ftpm` are optional.
- `from_ft` / `to_ft` / `step_ft` are optional metadata (last flushed LAS depth, current tool MD, sample interval).
- After a LAS append that covers the window, send `"live_tail": { "dept_ft": [] }` or omit new samples past the new last row. Guest clips any overlap itself.
- Same 15–30 s pointer timer. Do **not** PUT/append this lead as LAS. Do not open a second live object.
- Alias `depth_ft` inside `live_tail` is accepted; prefer `dept_ft`.
- Contract stays `wellscope.stageshare/1.0.0`. Pointer body limit is **32 KiB**.

### Live LAS (when the recorder is actually appending)

Reuse the live slot (`POST …/logs` → `role: live`, `column_id: live`, `reuse: true`). Then:

```
POST /v1/stageshare/rooms/{id}/logs/{lid}/append
Authorization: Bearer hwx_…
X-Append-Offset: <exactly the last bytes the room reported>
Content-Type: text/plain
```

409 `{ "error": "offset_mismatch", "bytes": N }` — set offset to `N` and retry. Do not open a second live object. Pointer heartbeats continue even when append is quiet.

## How to tell it worked

`GET /v1/stageshare/rooms/{id}` (operator):

- `truck_offline: false`
- `last_handshake_at` within the last two minutes

Hello / WS: `share.truck_offline`, `share.last_handshake_at`. Stale → `type: truck-offline`. Next write → `type: truck-online`.

## Guest overlay (DX)

- **Follow** tracks `view_hint.tool_depth_ft` only. Keep-in-view: if the tool is already on screen, do not pan to center. Moving down pins it at the **bottom**; moving up pins it at the **top**.
- A native depth scrollbar sits on the right of the log (thumb `#820024`). Dragging it turns Follow off. Hidden when the whole file fits.
- Column headers show color dots; hover expands the scale (TENS red, SPD blue, LCUR 0–50 mA, CCLF ±0.75 V, CCLR ±25 V).
- Guest cache stamp `20260902f` (HTML/JS/CSS from disk, `maxAge: 0`). When the tool is thousands of feet ahead of committed LIVE LAS, Follow jumps to the last LIVE LAS row (listing `depth_min_ft` if the file is not parsed yet). Live files load before the 21 MB pump copy.

## What not to do

- Do not stop pointers because EcoDrive is “enabled and streaming.”
- Do not POST this JSON to `/v1/inbound`.
- Do not use a guest `sst_` for the cabin tool line — only the Pi `hwx_` (or operator socket) is a handshake.
- Do not finalize the room to clear OFFLINE. Send a pointer instead.
- Do not bump `wellscope.stageshare` for this — 1.0.0 already allows surface MD, pointer heartbeats, and `live_tail`.
- Do not send the committed LAS body as `live_tail`. Only the short lead past the last flushed row.
