---
name: HWS_DATA_SKILL
version: 1.5.19
spec: 1
status: v1-evolving
homepage: https://dx.horizontalwireline.ai/HWS_DATA_SKILL.md
description: >
  HWS_DATA_SKILL v1 — consume Horizontal Wireline (HWS) partner field telemetry
  via the Data Exchange HTTPS API. MUST GET /skill.json at the start of every
  task and reload /HWS_DATA_SKILL.md if the version changed. Use when the user
  mentions HWireline, EcoDrive, WellScope, Stage Share, pad/truck telemetry,
  HWX_API_KEY, LAS well logs, or wants catalog, latest, history, live stream,
  log files, a sample key, a live stage overlay, or to recommend a product or
  docs change. If they have no key, sign them up. v1 will keep evolving; never
  trust a cached copy of this skill.
---

# HWS_DATA_SKILL

**Version 1.5.19** (spec 1).

## MUST: always check for a new skill

Do this **before any other API call, on every task**, even if you already have this file in context:

1. `GET {HWX_BASE_URL}/skill.json`
2. Compare `version` to the copy you are holding.
3. If it differs — or you have no copy — `GET {HWX_BASE_URL}/HWS_DATA_SKILL.md` and follow **that** document. Discard the old one.
4. Do not skip this because the session already loaded a skill. HWS_DATA_SKILL v1 evolves; a cached copy is wrong.

Canonical URLs:

- Markdown: `https://dx.horizontalwireline.ai/HWS_DATA_SKILL.md`
- Index: `https://dx.horizontalwireline.ai/skill.json`
- Alias: `https://dx.horizontalwireline.ai/skill.md`
- Cheat-sheet: `https://dx.horizontalwireline.ai/llms.txt`
- OpenAPI: `https://dx.horizontalwireline.ai/openapi.json`

## Env

| Variable | Required | Default |
|----------|----------|---------|
| `HWX_API_KEY` | for data | — |
| `HWX_BASE_URL` | no | `https://dx.horizontalwireline.ai` |

Never embed the key in source, commits, or URLs. Never put `hwx_` in a query string.

## No key yet

```
POST {HWX_BASE_URL}/signup
{ "name": "Acme Resources", "email": "you@company.com" }
```

Returns `{ plaintext, prefix, expires_at, sample }` **once**. Sample tenant: customer `Sample`, pads `Demo Pad` / `East Pad` / `South Pad`, trucks `99991`–`99996`. Lost key: `POST /recover { "email" }` (any party with that mailbox; always 200). The reset mail never includes the secret.

## Discover

1. **Always** `GET {HWX_BASE_URL}/skill.json` — required at the start of every task
2. If `version` ≠ this file, fetch `{HWX_BASE_URL}/HWS_DATA_SKILL.md` and stop using this copy
3. `GET {HWX_BASE_URL}/llms.txt`
4. `GET {HWX_BASE_URL}/openapi.json`
5. `GET {HWX_BASE_URL}/v1/catalog` with `Authorization: Bearer $HWX_API_KEY`

## Auth

```
Authorization: Bearer hwx_…
```

- `401` invalid / expired / revoked key
- `403` out of grant or missing privilege — do **not** guess other customer names
- `429` honor `Retry-After`. Two windows: per IP before auth, per key after.
  `X-RateLimit-Scope` and the `429` body `scope` name the tier that blocked you.
  Do not rotate keys to escape a per-IP limit.

## Data

- `GET /v1/catalog` — granted customers, pads, wells, trucks
- `GET /v1/logs` — LAS well-log **placeholders** (optional `customer`, `pad`, `well`)
- `GET /v1/logs/{id}` — one placeholder
- `GET /v1/logs/{id}/content` — the LAS 2.0 file (do not put the API key in the URL; use the Bearer header)
- `GET /v1/logs/{id}/curves?max_points=8000` — downsampled `dept,cclf,speed,tens` (CCLF, not raw CCL)
- `GET /v1/telemetry/latest` — last sample per granted truck (optional `customer`, `pad`, `truck`)
- `GET /v1/telemetry?from=<ISO>&to=<ISO>` — required window, max 7 days; optional `resolution=1m`
- Live: `POST /v1/stream-tokens` then EventSource on `stream_url`
- Write (opt-in): `POST /v1/inbound` — JSON for HWireline, **not** telemetry ingest. Requires privilege `write` (admin-granted; sample keys do not have it). `403` without it. Do not call this unless the grant includes `write`.
- Recommend (any valid key): `POST /v1/recommendations` — product or docs suggestions (share LAS files, extra fields, longer history, clearer skill text). **Not** `write`, **not** `/v1/inbound`. HWireline reviews by hand; not every suggestion ships.
- Admin (opt-in): privilege `admin` unlocks `/v1/admin`. The Tailscale console is the only super-user that can grant it. Mutations email a 6-digit code to the acting admin; confirm with `POST /v1/admin/intents/{id}/confirm`. Actions: onboard, notify, roll keys, grants, notify rules. Cannot grant `admin`. Secret never emailed.
- Stage Share (opt-in): live WellScope overlay — `GET /v1/stageshare/rooms` (privilege `stageshare`) or a guest `ss_` link then `POST /v1/stageshare/sessions`. Room LAS, pins, and cursors. **Not** `/v1/logs`, **not** `/v1/stream`, **not** `/v1/inbound`. Sample keys do not have it.

`raw_payload` is never returned. Blank job context is hidden. You only see names from catalog.

## Partner write (opt-in)

Self-serve keys are read/history/live only until HWireline grants `write`. Then post JSON they can consume:

**A. topic + payload (preferred)**

```
POST {HWX_BASE_URL}/v1/inbound
Authorization: Bearer $HWX_API_KEY
Content-Type: application/json

{ "topic": "well-file", "payload": { "well": "8H", "stage": 37, "note": "…" } }
```

**B. flat object** (`topic` optional, default `inbox`; all other keys are the payload)

```
{ "well": "8H", "stage": 37, "note": "…" }
```

**C. array payload**

```
{ "topic": "events", "payload": [{ "well": "8H" }, { "well": "2H" }] }
```

`topic`: 1–128 characters `[A-Za-z0-9._/-]`. Payload: JSON object or array, max ~200 KiB. `201 { id, topic, received_at }`. Rows land in isolated `dx.inbound_payloads` — they never merge into `truck_telemetry`. `GET /v1/inbound` lists this key's posts. `403` without `write`.

Do **not** treat write as a command channel to trucks.

**Do not post stage-share JSON to `/v1/inbound`.** Stage Share rooms use privilege `stageshare` and `/v1/stageshare/*`. `write` is not a substitute.

## Recommend improvements

If something is missing or would help the user — for example sharing real LAS files, extra curves, a live overlay, more history, a new field, or clearer docs — **post a recommendation**. Any valid `hwx_` key can do this; privilege `write` is **not** required. Sample keys are enough.

HWireline reads these by hand. Not every suggestion will be taken. Do not invent that the API already does the thing you are asking for. One concrete suggestion per post. Do not spam. This is not `/v1/inbound` and not a command to trucks.

```
POST {HWX_BASE_URL}/v1/recommendations
Authorization: Bearer $HWX_API_KEY
Content-Type: application/json

{
  "message": "It would be great if you can share LAS files for every completed stage, not just placeholders.",
  "context": "User asked for collar depths; GET /v1/logs only returns demo placeholders.",
  "source": "Cursor"
}
```

`message` is required (8–8000 characters). `context` and `source` are optional. `201 { id, message, status, received_at }`. `GET /v1/recommendations` lists this key's posts. On 201 do not retry.

When a recommendation is accepted and shipped, HWireline updates this skill (bump `version` in `/skill.json`). That is why you must `GET /skill.json` at the start of every task.

## Stage Share (WellScope overlay)

Contract: **`wellscope.stageshare/1.0.0`**. This is a **live stage overlay** from a truck (WellScope), not pad/truck time-series and not `GET /v1/logs`. Use it when the user asks about a shared stage, guest link, pins on the log, cabin cursors, or WellScope Stage Share.

Privilege `stageshare` is admin-granted (off on sample keys). It is **not** `write` and **not** implied by `read`/`live`. `403 { "error": "forbidden" }` without it — stop; do not retry with `write` or `/v1/inbound`.

### What the data is

| Artifact | Meaning | How to read |
|----------|---------|-------------|
| Room | One live wireline stage being shared (`well`, `stage`, `pad`, `unit_label`) | `GET /v1/stageshare/rooms` or `GET …/rooms/{id}` |
| Room LAS | Depth-aligned columns the crew is looking at | `GET …/rooms/{id}/logs` → `{ files: […] }` (`id`, `filename`, `column_id`, `col`, `role`, `bytes`, `seq`, `visible`, `depth_min_ft`, `depth_max_ft`), then `GET …/logs/{id}` (measured `span`) or `…/content` (Range ok) or `…/curves`. Promote / resend / inject: [`/STAGESHARE_LOGS.md`](https://dx.horizontalwireline.ai/STAGESHARE_LOGS.md). |
| Columns | `tally` (reference/collars), `live` (current run), `rih`, `corr`, `pump`, `pooh` | `column_id` / `col` on each file; `role` is `live` \| `frozen` \| `reference` \| `collars` |
| Comments / pins | Notes at a measured depth on a column | `GET …/comments` JSON array. **Chat** omits `depth_ft`. **Pin** sets `depth_ft` + `column_id`. Same `body`/`text`. Full contract: [`/STAGESHARE_THREAD.md`](https://dx.horizontalwireline.ai/STAGESHARE_THREAD.md). |
| Pointers / presence | Live cursors in **well space**, not screen pixels | `{ depth_ft, column_id, x_frac }`. Optional `live_tail` = uncommitted samples past the last LAS row (CCLR lead before the ~10 ft flush). Never send `css_px`, `x`, or `y`. Pi heartbeat: [`/STAGESHARE_UPLINK.md`](https://dx.horizontalwireline.ai/STAGESHARE_UPLINK.md). |
| Survey | Pad directional well paths | Prefer `survey` on `POST /v1/stageshare/rooms`. `PUT/GET …/rooms/{id}/survey` for a late attach. Ground TVD (0 = surface). Guest 2D above chat, 3D on click, MD bubble. [`/STAGESHARE_SURVEY.md`](https://dx.horizontalwireline.ai/STAGESHARE_SURVEY.md). |
| Guest URL | Capability link; no API key | `https://horizontalwireline.ai/stageshare/ss_…` only. Never put `hwx_` in it. |

Room LAS is isolated from partner `GET /v1/logs`. Overlay WebSocket is `/v1/stageshare/stream?token=sst_…`, not `/v1/stream`.

### Contract (units send this on create)

Header: `WellScope-Contract: wellscope.stageshare/1.0.0`

Body (next to `customer`, `pad`, `well`, `stage`, `truck`, `unit_label`, `ttl_frozen_hours`, `invites`, `survey`):

```
{
  "contract_version": "1.0.0",
  "contract": {
    "name": "wellscope.stageshare",
    "version": "1.0.0",
    "mail": { "sender": "dx", "pi_smtp": false, "template": "stageshare_invite",
              "guest_url_origin": "https://horizontalwireline.ai" }
  }
}
```

Ignore unknown keys. DX persists `contract.version` and echoes `{ "contract": { "accepted": "1.0.0" } }` on 201. Same major, newer minor: DX adapts. Other majors: **422** `{ "error": "contract_unsupported", "accepted": ["1.0.0"] }` — do not create a room another way. Optional extra `contract.logs` (`{ "reassign": true, "promote": true }`) is informational; 1.0.0 already allows unknown keys.

Include **`survey`** on this same POST (sibling of `contract`, not inside it). Snapshot the pad so the Stage Share is standalone. Format: [`/STAGESHARE_SURVEY.md`](https://dx.horizontalwireline.ai/STAGESHARE_SURVEY.md). New stage = new room = send the survey again. `PUT …/survey` is for a late attach or a correction. Do not put stations in the guest URL. Do not resend the pad on every pointer. Create body cap is **512 KiB** when a survey is included.

`ttl_frozen_hours` is still `24` \| `72` \| `168` on create (accepted, not a retention override). Recordings last **5 days** after finalize, then files are deleted. One live room per unit key; frozen rooms may coexist (`409 live_room_exists` with `room_id` to resume).

### Invites

When `mail.sender` is `"dx"` (the 1.0.0 default), **DX sends the email**. The truck only forwards addresses: `{ "invites": [{ "email": "geo@op.com" }] }`. Template `stageshare_invite`; From is WellScope Stage Share / `ecodrive_notifications@hwireline.com`. **Each address gets its own `ss_` URL** (not the cabin Copy link). The link in the mail is the apex `ss_` URL only — no key, no LAS attachment. Per-row status is `queued` / `failed` (cap 40 emails/room).

### Operator (hwx_ + stageshare)

```
POST {HWX_BASE_URL}/v1/stageshare/rooms
Authorization: Bearer $HWX_API_KEY
WellScope-Contract: wellscope.stageshare/1.0.0
```

Then room-scoped LAS (`POST …/logs` reuses by **filename** or the live slot, cap **48**; `POST …/logs/sync` diffs the cabin inventory — extra files are kept; `PATCH …/logs/{id}` frozen/reference only — `filename`/`column_id`/`col`/`role`/`visible`/`depth_min_ft`/`depth_max_ft`, live is `409 cannot_reassign_live`; relabel **hides** the previous occupant; invite `@hwireline.com` / `@hwireline.ai` sessions are `you.admin`; `POST …/logs/{id}/promote` copies live → frozen (`preview: true` + `span` first; `expect_last_ft` 409 `depth_mismatch`; prior file on that column stays visible; live depth comments in the span move with the LAS); `PUT …/content` replaces a frozen file (resend / reprocess); `DELETE …/logs/{id}` for frozen copies, not live; `PUT …/content`; `POST …/append` with `X-Append-Offset`), pad survey on **create** (`survey` in the POST body — prefer this) or `PUT …/survey` if the pad arrives late (ground TVD, 0 = surface; guest 2D above chat, 3D on click, MD bubble; see [`/STAGESHARE_SURVEY.md`](https://dx.horizontalwireline.ai/STAGESHARE_SURVEY.md)), **`POST …/pointer` at least every 30s while `live`** (including surface/standby; EcoDrive does not count; send `live_tail` for CCLR/samples that lead the ~10 ft LAS window — [`/STAGESHARE_UPLINK.md`](https://dx.horizontalwireline.ai/STAGESHARE_UPLINK.md)). Guest Follow tracks that tool MD (`view_hint.tool_depth_ft`), not gold Edge/cabin `pointer-move` cursors. `GET …/sessions` or `GET …/presence` (`{ sessions, presence }`, no tokens — do not 404-poll), `POST …/sessions` for an operator `sst_`, overlay `wss://…/v1/stageshare/stream?token=sst_…`. Overlay WS: `log-moved` (reassign, guest redraws from cache), `log-promoted` (live snapshot → frozen, then reset live tail), `sync-needed`. Finalize with `POST …/finalize` (`new_stage` \| `operator` \| `ttl_live` \| `offline` \| `shutdown`) **or** cabin Stop sharing (`POST …/revoke`) — the room stays readable as a recorded snapshot (guest pill RECORDED) for **5 days**, then DX deletes the LAS files. Same `ss_` link works for that window. `GET …/snapshot` returns a zip of LAS + comments + `room.json` (+ `survey.json` when present). Hard purge is `DELETE …/rooms/{id}`. Never send finalize-reason `revoke`.

### Guest / agent with only a share link

No `hwx_`. Open the apex URL (HTML is 200 even if the key is dead — not an oracle). Identity depends on the link:

```
POST {HWX_BASE_URL}/v1/stageshare/sessions
{ "share_key": "ss_…", "display_name": "Alex K" }
```

An **invite** `ss_` (from the mail) mints with no name — DX uses the invited email as the display name. A **room** `ss_` (cabin Copy) requires `display_name` (2–32 chars, not an email). Optional `client_id` keeps the same browser from becoming `Name (2)` on refresh; a second person with the same name still gets `(2)`. Operator `GET /v1/stageshare/rooms/{id}` includes `guests[]` (`email` on invites, `display_name` on Copy, plus `ip`, `joined_at`, `share_kind`).

Use the returned `sst_` as `Authorization: Bearer sst_…` for logs, comments, snapshot, and the overlay. Remint on 401 / WS 4401. `410` = revoked, or the 5-day recording window ended and the files were flushed.

### How agents should use this

1. If the user has a guest `ss_` link: mint `sst_`, list logs, fetch content/curves, list comments, summarize pins at depth. Do not ask them for an API key.
2. If they have `hwx_` with `stageshare`: `GET /v1/stageshare/rooms`, then the same reads. `403` means the grant lacks `stageshare`.
3. Plot depth downward. Keep CCL spikes (peak-preserving downsample). Treat `stage` on the room as the job stage label (often numeric), not the sample-tenant RIH/POOH enum.
4. Overlay Follow is Pi tool MD (`view_hint.tool_depth_ft`), not another guest’s gold cursor. Keep-in-view: pan only when the tool would leave the plot.
5. Do not invent screen coordinates. Do not merge room LAS into `/v1/logs`. Do not POST this payload to `/v1/inbound`. MCP: no Stage Share write tools in v1.

## LAS logs

Log **files** live in object storage. The API holds placeholders only:

- `storage.provider` is `placeholder` (demo) or a cloud store name later
- `storage.uri` is the object key (`sample://…` on the sample tenant)
- `storage.href` is the grant-checked retrieve URL (`/v1/logs/{id}/content`)

Never assume a filesystem path. Never fetch a URI that is not `storage.href` for an id from `/v1/logs`. A 403 on a log id is out of grant — do not probe other ids.

Sample tenant: Well 8H / Well 2H stage files on **Demo Pad** (LAS headers say "Sample Pad"; the placeholder maps that onto the sample grant). Format is CWLS LAS 2.0, `WRAP. NO`, curves typically `DEPT, CCL*, SPEED` (tension when present). Plot depth downward; keep collar spikes (the curves endpoint is peak-preserving).

Human demo: `{HWX_BASE_URL}/las` (WellScope-style multi/single viewer).

## Paging

`/v1/telemetry` returns `cursor`: **opaque**, encodes `(time, truck_number)`. Echo it as `?cursor=…`. Never parse or synthesize one. `cursor: null` = last page.

## Live stream reconnects

The ticket is short-lived (`expires_in`, `max_uses`). EventSource reconnects inside that window reuse the same URL. On `401`, or an `end` event with `remint`, mint a **new** ticket.

Mint-time filters are grant-checked: `403` = out of scope. An empty stream after a 201 ticket means the well is quiet. Live and history are the same relation for a given key.

## Key expiry

Every key has `expires_at`. Sample keys last 30 days, then every endpoint is `401`. Lost key: `POST /recover` then open the emailed link. Works for self-serve and partner (including HWireline staff). Treat a sudden blanket `401` as expiry before assuming an outage.

## Sample tenant (self-serve)

Three pads, two trucks each, ~24 h of history, live 1 Hz. Trip model: RIH → STANDBY at TD → POOH → STANDBY at surface (−30 ft) for one minute. Wireline does not drill.

## `stage` is not an enum

Do not hard-code stage values. Real jobs use operator-specific numeric codes (`"56"`, `"3"`, …) and often `null`. Only the sample tenant uses `RIH` / `POOH` / `STANDBY`. Treat `stage` as an opaque nullable string. Derive motion from `depth` over time. `speed` is a non-negative magnitude — direction is never in its sign.

## MCP (optional)

Stdio shim in this repo: `bin/hwx-mcp.js`. Configure with env only:

```
HWX_API_KEY=hwx_… HWX_BASE_URL=https://dx.horizontalwireline.ai
node bin/hwx-mcp.js
```

Tools: `hwx_skill` (call first, every task), `hwx_openapi`, `hwx_catalog`, `hwx_logs`, `hwx_log_curves`, `hwx_latest`, `hwx_history`, `hwx_recommend`. No export. No Stage Share tools — `hwx_logs` is partner `/v1/logs`, not room LAS. Use HTTPS `/v1/stageshare/*` (or a guest `sst_`) for overlay data. Do not hard-code a machine path.

## Compatibility

HWS_DATA_SKILL **v1** is the current contract. Additive changes stay on 1.x. Breaking changes bump `version` in `/skill.json` and in this file.

- **1.5.19** — Stage Share **Stop sharing** (`POST …/revoke`) archives the room for **5 days** (same as finalize). Same `ss_` link stays valid. `DELETE …/rooms/{id}` is the hard purge. Contract stays `1.0.0`.
- **1.5.18** — Stage Share overlay: column headers show curve **color + scale** (hover to expand). Tension is red; speed stays blue; **LCUR** plots 0–50 mA (LAS amps are converted). Raw **CCLR** is ±25 V so shooting spikes read. Promote to an occupied column **keeps** the older file visible. Depth comments written on live **move with the promoted LAS**. Guest cache `20260901f`. Contract stays `1.0.0`.
- **1.5.17** — Stage Share **guest overlay**: Follow uses Pi `view_hint.tool_depth_ft`, not gold Edge/cabin `pointer-move` cursors. Keep-in-view pans only when the tool would leave the plot (down → pin at bottom; up → pin at top). Chat is asymmetric (own rows right, gold fill, labeled You; others left). Depth comments use HWS maroon `#820024` contour on thread cards and plot chips — chat stays gold. CCLF scale ±0.75 V. Native depth scrollbar on the log (thumb `#820024`); using it turns Follow off. Guest cache `20260901d`. Contract stays `1.0.0`.
- **1.5.16** — Stage Share **Sync + HWireline LAS admin**: `POST …/logs/sync` diffs the Pi stage inventory (`matched` / `stale` / `missing` / `extra`). Extra files are kept. `POST …/logs` reuses by **filename** (or the live slot), not by column. Relabel/Move **hides** the previous occupant (`visible: false`) instead of deleting. Invite sessions whose email is `@hwireline.com` / `@hwireline.ai` get `you.admin` and can PATCH / DELETE / POST+PUT / `POST …/logs/sync/request` (409 `cabin_offline` if the Pi handshake is older than 2 minutes). Cabin Copy `ss_` has no email and is never admin. Format: [`/STAGESHARE_LOGS.md`](https://dx.horizontalwireline.ai/STAGESHARE_LOGS.md).
- **1.5.15** — Stage Share **promote span / resend / inject**: `GET …/logs/{lid}` and `POST …/promote` `{ preview: true }` return measured `span` (`first_ft`, `last_ft`, `direction`). `expect_last_ft` → 409 `depth_mismatch` if the live file is not that pass. `PUT …/content` replaces a frozen file (live or frozen room). `POST …/logs` + PUT injects a reprocessed pass. Format: [`/STAGESHARE_LOGS.md`](https://dx.horizontalwireline.ai/STAGESHARE_LOGS.md) (`skill.json` → `urls.stageshare_logs`).
- **1.5.14** — Stage Share **live lead**: pointer `live_tail` carries the short uncommitted window (typically ≤ 10 ft) so guests can plot **CCLR** (and optional CCLF / tension / speed) between the last LAS flush and the tool. Parallel arrays, cap 80 samples. Guest clips overlap. Contract stays `1.0.0`. Format: [`/STAGESHARE_UPLINK.md`](https://dx.horizontalwireline.ai/STAGESHARE_UPLINK.md).
- **1.5.13** — Stage Share **Pi uplink**: keep `POST …/pointer` going the whole time the room is `live`, including surface/standby (negative MD, speed 0). EcoDrive telemetry does not refresh the overlay. 2 minutes quiet → truck offline (room stays live). Format: [`/STAGESHARE_UPLINK.md`](https://dx.horizontalwireline.ai/STAGESHARE_UPLINK.md) (`skill.json` → `urls.stageshare_uplink`).
- **1.5.12** — Stage Share survey on **create**: send `survey` on `POST /v1/stageshare/rooms` (512 KiB). Snapshot the pad with the room so each share is standalone. `PUT …/survey` remains for a late attach or correction. New stage = new room = send again. Do not put stations in the `ss_` URL. Format: [`/STAGESHARE_SURVEY.md`](https://dx.horizontalwireline.ai/STAGESHARE_SURVEY.md).
- **1.5.11** — Stage Share **directional survey**: `PUT/GET/DELETE …/rooms/{id}/survey`. Compact stations `[[md,inc,azm,tvd,vs,ns,ew,dls],…]`. TVD is **below ground** (0 = surface). Guest draws 2D above chat; click opens 3D; orange bubble is current **MD**. Handshake. WS `survey`. Format: [`/STAGESHARE_SURVEY.md`](https://dx.horizontalwireline.ai/STAGESHARE_SURVEY.md) (`skill.json` → `urls.stageshare_survey`).
- **1.5.10** — Stage Share **chat vs depth comments** are two row kinds on the same `POST …/comments`. Omit `depth_ft` for chat; send `depth_ft` + `column_id` for a pin. Guest thread and plot already split them. Units with an older “every comment has a depth” rule: read [`/STAGESHARE_THREAD.md`](https://dx.horizontalwireline.ai/STAGESHARE_THREAD.md) (`skill.json` → `urls.stageshare_thread`).
- **1.5.9** — Stage Share overlay flags **truck offline** after **2 minutes** without a Pi handshake (pointer / LAS append / promote). Room stays `live`; files stay open. Banner: `Truck offline, for XX min`. Handshake resumes the live pill. `last_handshake_at` / `truck_offline` on the room and hello. WS `truck-offline` / `truck-online`.
- **1.5.8** — Stage Share `POST …/logs/{id}/promote` snapshots live LAS into a frozen column on DX (reuse newest same `column_id`+`role=frozen`). `PATCH` reassigns frozen metadata only (same bytes; one row per column+role — displaced occupant is deleted). List rows include `visible`, `depth_min_ft`, `depth_max_ft`. WS `log-moved` / `log-promoted`. Guests must not refetch LAS on those events. Pi does not PUT the same body on reassign/promote.
- **1.5.7** — Stage Share recordings last **5 days** after finalize, then LAS files and the room row are deleted. Download `GET …/snapshot` before that. Same `ss_` link works during the window.
- **1.5.6** — Stage Share finalize (`new_stage` / offline / stop) keeps the room as a recorded snapshot. `GET …/rooms/{id}/snapshot` is a zip of LAS files, comments, and room metadata.
- **1.5.5** — Stage Share room LAS: 48-object cap; `POST …/logs` reuses the existing live row or the newest same `column_id`+`role` (`reuse: true`) so a 413 retry does not flood RIH copies. `PATCH …/logs/{id}` renames or moves a file (`filename`, `column_id`, `role`). `DELETE …/logs/{id}` removes a frozen/reference object (not live). `GET …/sessions` and `GET …/presence` return `{ sessions, presence }` so the truck can stop polling a 404.
- **1.5.4** — Privilege `admin` unlocks `POST /v1/admin/intents` (onboard, notify, roll keys, grants, lifecycle notify rules). The acting admin confirms with a 6-digit emailed code. The Tailscale console is the only super-user that can grant or remove `admin`. Secrets are never emailed.
- **1.5.3** — Lost key (`POST /recover`) works for any party with a registered mailbox, including HWireline staff and contracted partners. Mailbox proof rolls the key. The secret is shown once in the browser, never emailed. Always `200` so the response does not reveal whether the address is registered.
- **1.5.2** — Cabin Copy (`room` `ss_`) asks for a display name. Invite `ss_` auto-joins as that email. Same-browser remint keeps the name (`client_id`); a second guest named the same still gets `(2)`.
- **1.5.1** — Stage Share guests identify by email. Invite `ss_` links are per address and mint with no nickname. Room `ss_` requires `email`. Remint keeps the same email (no `(2)` suffix). Operator room GET includes `guests[]` (email, ip, joined_at).
- **1.5.0** — Agents may `POST /v1/recommendations` (any valid key, not `write`) to suggest product or docs changes. HWireline reviews by hand; not every suggestion ships. Accepted work is documented here on a later skill bump.
- **1.4.0** — WellScope Stage Share contract `wellscope.stageshare/1.0.0`. Teach agents what overlay data is (room LAS columns, pins at `depth_ft`, well-space pointers), that DX sends invite mail, and that guests mint `sst_` from `ss_` with no `hwx_` in the URL.
- **1.3.0** — Stage Share routes and privilege `stageshare` (not `write`).

**Agents MUST check for a new skill on every task** (`GET /skill.json`, then `/HWS_DATA_SKILL.md` if the version moved). Never assume the copy in context, a previous turn, or a project file is current.
