# Gate 1 · Clean-room 编码所有权参考实现

> **参考实现，不是个人作品。** 运行测试、复制代码或让 AI 一次性生成相同仓库，不能证明你拥有这项能力。只有你能从空目录独立复现核心纵向切片，现场修改、测试、注入故障、定位根因并无稿解释取舍的部分，才可以进入个人留下了什么。

这个小实验只使用公开、合成的城市设施工单。它不包含雇主、客户或任何真实业务数据，也不代表生产系统、客户上线或业务收益。

## 先看失败场景

一个状态写入请求在外部系统已经提交后超时。若代码把 `TimeoutError` 直接当作“写入失败”，再换一个请求 ID 重试，就可能产生两次副作用。这个参考实现把模型或 API 请求视为提案，并用确定性代码完成：

1. Pydantic 请求/响应验证；
2. 独立审批信封对 `operation_id + action + args_digest + expiry` 的绑定；
3. 同一次业务意图始终复用稳定 `operation_id`；
4. 只对白名单瞬时错误做最多 2 次尝试；
5. 提交后超时先按 `operation_id` 对账，不盲目创建新操作；
6. 写入成功后重新读取外部事实并核对 receipt；
7. 每一步留下不含审批签名和业务正文的结构化 JSON trace。

## 代码边界

```text
FastAPI / Pydantic
        │ typed request
        ▼
OwnershipService
  ├─ verify approval + exact args digest
  ├─ bounded write attempts
  ├─ reconcile unknown outcome
  └─ readback verification
        │
        ├── CaseReadTool
        │     ├─ HttpCaseReadTool: 真实 async HTTP 边界
        │     └─ SyntheticCaseGateway: 测试替身
        │
        └── CaseWriteTool
              └─ SyntheticCaseGateway: 幂等 ledger + 故障注入
```

`HttpCaseReadTool` 展示真实 `httpx.AsyncClient`、wall-clock timeout、瞬时错误白名单和指数退避。默认 FastAPI 应用使用内存中的合成 gateway，保证没有云凭据也能复现；它不能证明真实云或客户环境交付。

## 运行

需要 Python 3.11+。

```bash
cd outputs/ai-agent-career-atlas/labs/clean-room-ownership
python3 -m venv .venv
source .venv/bin/activate
python -m pip install -e '.[dev]'
pytest -q
```

启动本地参考 API：

```bash
export CLEAN_ROOM_APPROVAL_SECRET='replace-with-a-local-secret-at-least-16-bytes'
uvicorn clean_room_ownership.app:app --reload --port 8010
```

可访问：

- `GET /`：明确的参考实现声明；
- `GET /health`：健康检查；
- `GET /v1/cases/CASE-CIVIC-001`：类型化异步只读工具；
- `POST /v1/cases/{case_id}/actions`：审批绑定、幂等写入与回查；
- `GET /v1/traces/{trace_id}`：本地结构化执行轨迹。

写接口不会提供“自助审批”HTTP endpoint。测试使用 `TrustedApprovalAuthority.issue(...)` 模拟独立控制面；真实系统必须把签发能力放在模型和执行器都不能伪造的服务中。

## pytest 验证了什么

| 场景 | 必须观察到的事实 |
| --- | --- |
| 正常写入 | 审批摘要匹配；写 1 次；receipt 与 readback 一致 |
| timeout before commit | 状态为 `not_started`；同一 `operation_id` 有界重试；最终只提交 1 次 |
| timeout after commit | 状态先为 `unknown`；按 operation ledger 对账；不发第二次写 |
| 重复请求 | 返回同一 receipt；`committed_writes` 仍为 1 |
| 审批后改参数 | 在进入写工具前拒绝；写调用计数为 0 |
| 外部结构错误 | Pydantic 拒绝脏响应；不把错误数据伪装成成功 |
| API 结构错误 | FastAPI 返回 422；不触发任何副作用 |
| async HTTP timeout | 仅在预算内重试；第二次成功后返回类型化对象 |

测试只证明当前代码满足这些不变量，不证明你能重写它，也不证明 Agent 质量、生产可靠性或客户价值。

## 60–90 分钟 Clean-room 实测

不要一边照抄本目录一边计时。先把参考实现关闭，从空目录开始；结束后再对照差异。

### 时间盒

- `0–10 分钟`：写出请求/响应、只读工具、写工具、审批和 operation 状态契约。
- `10–30 分钟`：完成 FastAPI + Pydantic 类型化 GET，以及一个真正的 async 外部读取边界。
- `30–55 分钟`：实现审批绑定写入、稳定 operation identity、幂等 ledger 与 readback。
- `55–70 分钟`：补正常、重复请求和审批不匹配测试。
- `70–90 分钟`：随机注入 timeout-before 或 timeout-after-commit；现场定位并修复，再增加一个约束或小功能。

### 最大交付物

- 可运行仓库和连续 Git commit；
- 测试报告；
- 一条成功 trace、一条注入故障 trace；
- `AI-USE.md`；
- 一页 ADR，解释为何超时不等于失败、为何审批绑定精确参数、为何重试不能生成新 operation。

### 过 / 有条件过 / 不过

**Pass**

- 从空目录完成核心纵切，所有关键测试通过；
- 60–90 分钟内现场增加一个工具或约束；
- 能制造并定位 timeout、重复写或结构错误中的至少一种；
- 无稿解释 approval、attempt、operation、receipt、trace 的区别；
- AI 只用于解释、局部建议或 review，代码演进和被拒建议可追溯。

**Conditional Pass**

- 主路径可运行，但需要提示才能修复一个关键边界；或测试缺少一类故障；
- 给出明确的 10–15 小时针对性补强计划，并在新题上复测。

**Fail**

- 让 AI 整仓生成后无法现场修改或解释；
- timeout 后换新 operation 重试、审批可由请求参数伪造，或重复请求产生两次写；
- 只看 HTTP 200，不检查 readback、trace 或外部事实；
- 测试失败被隐藏，或把参考实现写成个人从零作品。

## AI 使用记录

在自己的 clean-room 仓库新增 `AI-USE.md`，每次使用 AI 都追加一行：

| 时间 | 当时问题 | AI 建议摘要 | 接受 / 拒绝 | 你的判断与修改 | commit / test |
| --- | --- | --- | --- | --- | --- |
| 00:24 | timeout 后是否重试 | 建议统一重试三次 | 拒绝 | 写操作先标 unknown，并按稳定 operation 对账 | `abc123` / `test_timeout_after_commit` |

至少保留：

- AI 建议了什么；
- 你选择了什么；
- 你否定了什么以及原因；
- 哪个故障暴露了原方案的问题；
- 哪个 commit 和测试证明修改有效。

“AI 写了，我看懂了”不是所有权证据。

## 常见错误与补救

- **错误：**在 retry loop 内生成 operation ID。**补救：**把 operation identity 提升到请求入口，并测试所有 attempt 完全相同。
- **错误：**请求带 `approved: true` 就执行。**补救：**使用独立签发、绑定精确参数和过期时间的审批信封。
- **错误：**提交后超时直接标失败。**补救：**保留 `unknown`，先查 operation ledger，再决定是否沿同一 identity 重试。
- **错误：**只断言响应码。**补救：**同时断言外部提交计数、receipt、readback 和 trace 状态序列。
- **错误：**重试 Pydantic 结构错误。**补救：**结构错误不是瞬时传输故障，立即停止并保存坏响应的脱敏摘要。

如果针对性补强后仍无法在新题中独立完成修改和排障，停止把该项目写成“本人拥有的 Agent”，先回到 10–15 小时编码补强，再参加第二次陌生题复测。

## 与 CivicOps Stage 2 的桥接

这个实验只证明一个窄的编码所有权基线。进入 CivicOps 单 Agent 时，保留相同的不变量：模型只提议结构化工具调用；2–3 个读取工具与 1 个审批写工具分离；证据冲突时停止；checkpoint 恢复复用 operation identity；写后回查；trace 覆盖检索、模型、工具、审批、执行与回查。不要在这个基线上增加多 Agent、多个编排框架、复杂 UI 或 Kubernetes。

