# EM 观察网页可视化阶段 02 交接

状态：完成

阶段目标：完成独立 `netstandard2.0` 可视化类库的受限 Loopback HTTP/1.1 GET/SSE 传输层与幂等会话门面；不包含网页资源、EM Adapter 或 MovementTest 集成。

基线提交：`106201e87b6144fe7cbff80f2994a6539cf15b98`（阶段 01 交接；`b0b79e5` 已核对为祖先）。

实现提交：`fa1a266` — `feat: serve planning snapshots on loopback`。

修改文件：

- `ClumsyPilot/TrajectoryPlanningVisualization/Runtime/LoopbackHttpRequestReader.cs`
- `ClumsyPilot/TrajectoryPlanningVisualization/Runtime/SseClientConnection.cs`
- `ClumsyPilot/TrajectoryPlanningVisualization/Runtime/LoopbackVisualizationServer.cs`
- `ClumsyPilot/TrajectoryPlanningVisualization/PlanningVisualizationSession.cs`
- `ClumsyPilot/tests/TrajectoryPlanningVisualizationVerificationHost/Program.cs`
- `ClumsyPilot/tests/TrajectoryPlanningVisualizationVerificationHost/ServerChecks.cs`

新增或确认的接口：

- `PlanningVisualizationSession.Start(PlanningVisualizationStaticSnapshot)` 返回含 `http://127.0.0.1:<port>/?token=<64-lowercase-hex>` 的 `PlanningVisualizationSessionInfo`；`Publish`、`Stop`、`Dispose` 和重复 `Start`/`Stop` 均具备本阶段规定的幂等边界。
- 服务器只以 `TcpListener(IPAddress.Loopback, port)` 监听；可用路由为带 token 的 `/api/bootstrap` 与 `/api/events`。请求仅接受 HTTP/1.1 `GET`，读取总时限为 2 秒、头部上限 16 KiB。
- `/api/events` 最多两个客户端；每个客户端以容量一发送槽和 1 秒写超时隔离慢客户端。单 dispatcher 在无浏览器时仍收集有界周期摘要，且只在有 SSE 客户端时序列化帧。
- `Stop` 最多提供 100 ms 发送 `event: end`（`会话已结束`），然后关闭 socket 和 listener；服务内部故障不从规划发布调用逸出。

验证命令与结果：

1. `git merge-base --is-ancestor b0b79e5 HEAD` — 退出码 0。
2. `git show --stat 55d8e1eebf592b03caff28dfee28745959e41e09` 与 `git show --stat ab8e4010f8d42dd8ddcd6066b82eaba7c163fc28` — 文件树与阶段 01 交接一致。
3. `dotnet run --project ClumsyPilot/tests/TrajectoryPlanningVisualizationVerificationHost/TrajectoryPlanningVisualizationVerificationHost.csproj` — 退出码 0，输出 `PASS trajectory-planning-visualization`；提交后重新执行结果相同。
4. `dotnet build ClumsyPilot/TrajectoryPlanningVisualization/TrajectoryPlanningVisualization.csproj` — 退出码 0，0 警告、0 错误。
5. 真实 `TcpClient` 检查已覆盖：403（缺 token）、404（授权未知路由）、405（POST）、400（HTTP/1.0 与超过 16 KiB 头）、503（第三 SSE 客户端）、UTF-8 `Content-Length`、256-bit token、慢 SSE 客户端下 10,000 次 `Publish` 在 1 秒内完成、无客户端时三条周期摘要、`event:end` 正常停止和端口重绑。
6. `git diff --check fa1a266^ fa1a266 -- <阶段 02 transport 文件>` — 退出码 0；`git diff-tree --no-commit-id --name-status -r fa1a266` 仅列出上述六个 Task 3 文件。

未运行的验证及原因：无。

已知警告或遗留问题：工作区仍有 130 个阶段范围外脏项；启动时和提交过程中均未展开、暂存、重置或修改它们。本阶段库构建无警告。

与原计划的偏差：无。为使超限头测试在 Windows 上可靠收到 400，解析器在拒绝其 16 KiB 上限后，仍在同一 2 秒总读取预算内丢弃至 `\r\n\r\n`；超限请求不被解析或接受。

下一阶段注意事项：阶段 03 仅实现原类库 Task 4–5 的嵌入中文网页、独立验证、冒烟模式和 README。它应使用本阶段 `PlanningVisualizationSession` 的 `/api/bootstrap` 和 `/api/events`，并维持所有路由 token 鉴权；不得引入 EM、MDCS、Painter 或 MovementTest 类型。
