# Intentional mobile knocker — thin proof

**Status:** docs + codec + proof on Ship (ops / client path). **Spec diff: none.**

Prove a phone-style caller can **lodge** into the existing frozen door without a native store app, app detection, or marketplace.

| Tool | HTTP |
|------|------|
| `gate.run` | `POST /v1/gate/run` |
| `gate.status` | `GET /v1/gate/status/{job_id}` |
| `gate.get_result` | `GET /v1/gate/results/{job_id}` |

Door prose (wire unchanged): **Lodged**=202 · **Recalled**=200 · **Occupied**/ **Contested**=409.

---

## What this is

1. **Share sheet / deep-link** — a `rivalatch://knock?…` URI encodes the **existing** `gate.run` JSON body (query keys map 1:1 onto frozen fields).
2. A thin client (Shortcuts, agent chat paste, MCP host) **parses** that URI and calls `gate.run` → polls `gate.status` → fetches `gate.get_result`.
3. **MCP path** — same decoded body via `tools/call` on the three frozen tools (stdio or `POST /mcp`). Isomorphic to HTTP.

No fourth MCP tool. No new GateResult / OpenAPI fields. No `/mobile` route invent.

---

## Deep-link (client-side only)

```
rivalatch://knock?idempotency_key=<uuid>&type=bundle&uri=fixtures&checks=smoke&base_url_path=/
```

Optional: `criteria=a,b` (comma-separated) → `acceptance.criteria`.

| Query | Maps to |
|-------|---------|
| `idempotency_key` | `idempotency_key` (required) |
| `type` | `app_ref.type` (default `bundle`) |
| `uri` | `app_ref.uri` (default `fixtures`) |
| `rev` | `app_ref.rev` (optional; default `null`) — e.g. git tip SHA |
| `checks` | `checks` CSV (default `smoke`) |
| `criteria` | `acceptance.criteria` CSV (default empty) |
| `base_url_path` | `acceptance.base_url_path` (default `/`) |

`app_ref.rev` is `null` unless the optional `rev` query key is present (existing frozen field — no Spec diff).

Git tip knock (public repo; Runner shallow-clones at `rev`, serves the Pages root, smoke `GET /`):

```
rivalatch://knock?idempotency_key=<uuid>&type=git&uri=https%3A%2F%2Fgithub.com%2FConWan30%2FQoresence&rev=<40-hex-sha>&checks=smoke&base_url_path=%2F
```

Codec: `vibegate_ship.mobile_knocker` — `build_deep_link` / `parse_deep_link` / `extract_deep_link` / `share_sheet_envelope`.

Share-sheet paste may wrap the URI in prose; `extract_deep_link` pulls the first `rivalatch://knock?…` token.

---

## Explicit non-goals

- Native App Store / Play Store binary
- App / UA detection or install banners
- Marketplace list/publish/crown surfaces
- Buying domains or committing secrets / challenge tokens
- Spec schema, OpenAPI, GateResult, or MCP tool invention

---

## Proof

```bash
# From vibegate-ship/ (API key never printed)
export VIBEGATE_API_KEY=dev-key
pytest -q tests/test_mobile_knocker.py
PYTHONPATH=src:../vibegate-runner/src python scripts/mobile_knocker_proof.py
# → /tmp/vibegate-ship/mobile_knocker_proof.json
```

Success evidence in the proof JSON:

- HTTP **Lodged** `202` from share/deep-link body → `gate.status` → `gate.get_result`
- MCP same three tools only; lodge / status / result
- `spec_diff: none`; `native_store_app` / `app_detection` / `marketplace` all false
