# Control Layer 0.2.2 acceptance

Date: 2026-10-03, Asia/Shanghai. Firmware and existing flash artifact remain **0.2.1**; no ESP32 connection, rebuild or flash was used.

## Pi proof

Installed SDK: **Pi 0.85.1**. Dedicated project/session only; no important user session was modified.

- Initial proof session: `01a0ff31-37f8-752c-8d5f-9e0b1aa589d3`.
- Ownership/process-cleanup proof session: `01a0ff39-0e43-71ef-8592-20cacb154b7a`.
- Final symlink/deduplication proof session: `01a0ff3c-c76b-718d-b6e2-0b99c11245d6` (0.205-second input acknowledgment).
- SDK first input `Reply exactly SESSION_READY` → Extension/Gateway observed SESSION_READY, DONE.
- Initial SDK host completely closed; new Gateway catalog returned **DORMANT**.
- `POST /api/agents/pi/sessions/{same-id}/input` with `Reply exactly SESSION_RESUMED` → accepted, `resumed:true`, **same ID**, response about **0.23 seconds**; Extension/Gateway observed SESSION_RESUMED, DONE.
- Another input on the existing Gateway-owned runtime returned `resumed:false`, and SESSION_LIVE was observed.
- Gateway stopped and restarted; same persisted session was rediscovered DORMANT.
- Additional external live-runtime proof: a separate SDK runtime loaded the globally symlinked pager extension, reported ready, then Gateway input returned `resumed:false` and EXTERNAL_LIVE_OK was observed on the same ID. No TUI was launched. This caught and fixed a Pi jiti-loader symlink-relative import issue; the installed single-file extension symlink remains supported.

`server/.venv/bin/python scripts/smoke-pi-control.py` reproduces the initial/persist/stop/dormant/resume/live-input/Gateway-restart chain using a fresh temporary project. It leaves a `pi-result.json` in its printed proof directory; persistent session files stay owned by Pi. It requires working Pi model/auth and the existing event credential, and makes real model calls. Ordinary regression tests do not make model calls.

## Codex proof

Installed CLI and existing daemon: **0.154.0**. Dedicated thread: `01a0ff2c-30b1-7f80-8863-9cb15616a118`.

Static list, exact cwd filter, read, same-ID dormant resume, turn input, assistant deltas, tool activity, settings update, and independent App Server stop/restart recovery all passed. Integrated Gateway → existing daemon input returned in about **0.256 seconds**, followed by GATEWAY_CODEX_RESUMED/DONE. A live turn emitted commandExecution for read-only `pwd` and CODEX_TOOL_OK. Detailed method evidence and lifecycle boundaries are in [Codex spike](codex-app-server-spike.md).

## Regression and deployment

`./scripts/test-server.sh`: **22 Python tests, 7 Node tests, real HTTP/WebSocket smoke passed**. Coverage includes project persistence/validation, static/live overlay and DORMANT, concurrent same-session resume/input, live runtime without control endpoint refusal, Gateway rediscovery, per-session ownership lease, transport crash/reconnect with no replay, Codex pagination/activity/approval rejection, and target dropdown focus/pending-request races. Existing machine auth and device/Pi activity smoke pass unchanged. The only warning is the installed Starlette/AnyIO BlockingPortal deprecation.

Production Gateway was restarted with repository-external SDK configuration and `GATEWAY_CODEX_ENABLED=1`. The prior environment file was backed up. Only the explicitly requested esp32-dev project was registered; no home-directory project scan was performed. Production `/health` returns control_version 0.2.2, and the unified catalog returned both Pi and Codex with no adapter errors. Existing Codex daemon was reused without restart or another service/port.

Anonymous public requests to `/admin`, `/api/projects`, `/api/agents`, `/api/projects/esp32-dev/sessions` and `/api/admin.js` returned Cloudflare Access 302 redirects. `/health` returned 200. Machine credentials and Tunnel routes were unchanged.

Approval and Voice remain design seams only. Existing Pi sessions require `/reload` to gain the updated control endpoint. Independent uninstrumented clients cannot be globally locked through Pi's SDK; shared-session concurrent use outside this control boundary is unsupported. Approval-required Codex operations are explicitly declined pending a future UI.

**PI STATIC SESSION CONTROL READY: YES**

**CODEX SESSION CONTROL READY: YES**
