# PJ2026-010403 API契约

## 修改历史

| 版本 | 对应 commit id | 更新日期 | 变更说明 |
| --- | --- | --- | --- |

当前正文仍在规格治理草稿中；未定稿前不新增版本号，不为单次编辑追加 `待提交` 版本。

## 正文

## PJ2026-010403 API契约需求规格

## 1. 文档控制

| 字段 | 内容 |
| --- | --- |
| 编号 | PJ2026-010403 |
| 短名 | API契约 |
| 层级 | L2 课题 |
| 状态 | 已生效 |
| 实现引用版本 | draft-2026-06-20-p0-workbench-pure-read-api; draft-2026-06-20-p0-error-diagnostics; draft-2026-06-22-p1-workbench-redis-derived-cache; draft-2026-06-27-p0-workbench-read-api-contract; PJ2026-010401080313 Workbench实时权威 draft-2026-07-14-p0-pure-kafka-authority; PJ2026-01050105 Web鉴权 draft-2026-06-18-p0-auth |
| 需求规格模板 | [ISO/IEC/IEEE 29148 需求规格模板](../../templates/iso-iec-ieee-29148-requirements-spec-template.md) |
| 上级规格 | [PJ2026-0104 客户端](PJ2026-0104-client.md) |
| 关联规格 | [PJ2026-010401080313 Workbench实时权威](PJ2026-010401080313-workbench-realtime-authority.md)、[PJ2026-01060505 Workbench性能](PJ2026-01060505-workbench-performance.md) |
| 规格治理索引 | [规格治理](spec-governance.md) |

本文采用 ISO/IEC/IEEE 29148 需求规格模板的项目裁剪版：正文只保留 API 契约的稳定使命、范围、术语、系统边界、内部分工和原子需求。

## 2. 目的和范围

### 2.1 目的

API契约负责定义 Cloud Web、HWLAB CLI 和自动化脚本共同使用的 RESTful HTTP API 入口语义，使同一用户请求在 Web、CLI 和 HTTP 调用下使用一致的 method、path、query/body、schema、鉴权主体、错误语义和结果标识。

本课题的目标状态是：客户端入口可以通过同源 API 证明业务路径是否可用；后端业务事实改变时，API 契约负责稳定暴露字段和错误，而不在客户端侧发明第二套事实模型。

### 2.2 范围内

- Cloud Web 同源相对 REST/JSON path、route policy 和绝对 URL 禁止边界。
- Web session、API key、AuthPrincipal、actor 摘要和权限错误在客户端入口中的表达。
- Agent session、chat、steer、cancel、result、trace 和 inspect 的用户可见 API 形态。
- CaseRun case list、run admission、run status、events、aggregate、artifact manifest、cancel 和 HWPOD/AgentRun 引用的用户可见 API 形态。
- 项目管理 navigation/projects/MDTODO source/file/task/workbench-link REST 资源、HWPOD-bound Source 配置、file content、task mutation、ProjectTask DTO、Workbench launch intent DTO 和独立项目管理服务代理边界。
- Workbench workspace、Admin Access、provider profile、HWPOD node-ops、gateway 和性能摘要等入口的字段稳定性。
- JSON 成功/失败响应、HTTP 状态、错误分类、脱敏、默认摘要和完整展开边界。
- Workbench pure-read API 的缓存诊断字段，包括 `cacheStatus`、`cacheAgeMs`、projection revision/seq、dbQueryAvoided 和 Redis unavailable/stale 的低基数表达。

### 2.3 范围外

- 用户、组织、API key、额度、role、OpenFGA relation 和账本真相归 [用户管理](PJ2026-0105-user-management.md)。
- AgentRun run、command、runner job、session continuation 和 provider runtime 归 [Agent编排](PJ2026-0102-agent-orchestration.md)。
- HWPOD 资源身份、硬件操作、node 路由和原始硬件事实归 [硬件池](PJ2026-0101-hardware-pool.md)。
- CaseRun、评价、aggregate、回放和训练反馈归 [HarnessRL](PJ2026-0103-harness-rl.md)。
- ProjectSource、MDTODO adapter、Markdown 任务投影、ProjectWorkbenchLink 和外部 PM adapter 归 [项目管理](PJ2026-010404-project-management.md)。
- 运行端点、FRP/Caddy、Secret、rollout 和 Prometheus 监控归 [平台运维](PJ2026-0106-platform-ops.md)。
- Redis workload、镜像、资源、NetworkPolicy、TTL 数值、metrics scrape 和运行面发布归 [平台运维](PJ2026-0106-platform-ops.md) 与 [Workbench性能](PJ2026-01060505-workbench-performance.md)；API契约只定义客户端可见字段和诊断边界。

## 3. 术语表

| 术语 | 定义 |
| --- | --- |
| 同源 API | Cloud Web origin 下的相对 REST/JSON HTTP API path，供浏览器和 HWLAB CLI 使用。 |
| route policy | 哪些 path 可以通过 Cloud Web 代理、哪些只能作为显式 Cloud API 诊断或内部服务调用的边界规则。 |
| AuthPrincipal | 后端根据 Web session 或 API key 恢复出的用户主体摘要。 |
| CanonicalPrincipal | `/auth/session` 和受保护业务 route 共享的最小身份结果，包含 `authMethod`、`identityAuthority`、`sessionKind`、actor 摘要和后端 capability 摘要；它是业务 route 的唯一身份输入。 |
| 业务标识 | sessionId、turnId、traceId、runId、commandId、caseId 等用户可继续查询的稳定标识；projectId、workspaceId 和 conversationId 只能作为筛选、metadata 或兼容映射字段，不作为前端鉴权 authority。 |
| CaseRun resource | Cloud Web origin 下表达 CaseRun case、run、stage、event、aggregate 和 artifact manifest 的 RESTful 资源集合。 |
| Project Management resource | Cloud Web origin 下表达项目、source、MDTODO 文件、任务、Workbench link 和项目能力的 RESTful 资源集合。 |
| HWPOD-bound Source DTO | 项目管理 API 中表达 `sourceKind=hwpod-workspace`、`hwpodId`、`nodeId`、workspace 相对根、capability、probe status 和脱敏 blocker 的 Source 配置对象。 |
| ProjectTask | 项目管理 API 返回的任务 DTO，包含 `taskRef`、`projectId`、`sourceId`、title、status、父子关系、linkCount 和脱敏 metadata。 |
| ProjectTask mutation | 对 MDTODO 任务 title/body/status/add/delete/reindex 的显式写入资源；必须带 expected revision 或 fingerprint，并由项目管理服务写入 Markdown source-of-truth。 |
| `taskRef` | 项目管理 opaque 任务引用；API 可以转发和保存，Workbench、Cloud Web 和 CLI 不得解析其内部结构。 |
| WorkbenchLaunchIntent | `POST /v1/workbench/launches` 的请求 DTO，用于把项目任务上下文传给 Workbench session admission。 |
| ProjectWorkbenchLink | 项目管理 API 返回的任务到 Workbench session/trace 的链接摘要，只表达 ID 指针和脱敏状态。 |
| Run admission | 接收 CaseRun 提交并返回 `202 Accepted`、runId、statusUrl 和下一步查询入口的短返回 mutation。 |
| selected origin mode | 客户端入口从 YAML 解析出的 origin 模式，至少区分 `public` 和 `internal`；API 只看到同源请求，不反推或硬编码入口地址。 |
| 错误语义 | HTTP status、错误码、blocker、failureKind、route 和下一步提示组成的可定位失败表达。 |
| W3C trace context | `traceparent` / `tracestate` / `baggage` 等跨进程传播上下文；API 响应只允许把 `traceparent` 和脱敏后的 OTel trace id 暴露给客户端，不向用户展示 `tracestate`、`baggage` 或上游私有上下文。 |
| OTel trace_id | OpenTelemetry/W3C trace context 中的 16-byte 非全零 trace id，以 32 位十六进制字符串表达；它用于从用户截图或复制信息跳转到 trace backend，不替代业务 `traceId`、`sessionId`、`runId` 或权限判断。 |
| HwlabErrorEnvelope | API 用户可见失败响应的统一 JSON envelope，承载错误 code、用户文案、分类、层级、retryable 和脱敏诊断字段。 |
| ErrorDiagnostic | 错误诊断字段集合，至少包含 OTel trace id、request id、route、serviceId、observedAt 和 `valuesRedacted=true`；它只用于排障和复制，不参与 Workbench lifecycle 或 final response 判断。 |
| Session detail | `GET /v1/workbench/sessions/{sessionId}` 这类按稳定 sessionId 或后端认可 opaque session handle 查询当前会话 metadata、消息指针和状态的 RESTful detail resource。 |
| Conversation detail | 迁移期兼容资源；可返回 conversation metadata、sessionId、threadId 和 message seed，但不得继续作为 Web deep link 或前端 active session authority。 |
| TurnSnapshot | 按 turnId 或 traceId 查询一次用户提交/steer/retry/cancel 生命周期、终态、message 指针和执行引用的 RESTful snapshot。 |
| TraceEventPage | 按 traceId 和 cursor/seq 分页查询 trace events 的 RESTful page；只表达 trace 明细，不决定主 timeline 的 final response。 |
| Message/Part | Web 和 CLI 共同渲染会话 timeline 的持久消息事实；message 表达角色、父子关系和终态摘要，part 表达文本、工具、文件、错误、trace 摘要等可组合内容。 |
| Timing metadata | Workbench DTO 中用于显示消息最近事件和轮次耗时的投影时间字段，至少包括 `startedAt`、`lastEventAt`、`finishedAt` 和 sealed `durationMs`；相对 age 只能由浏览器显示层用本地 now 临时计算。 |
| EventStream | Cloud Web origin 下的 SSE 实时入口，用于推送 server.connected、session、turn、message、part、trace 和 workspace lifecycle 事件。 |
| WorkbenchEventMapper | [PJ2026-010401080313 Workbench实时权威](PJ2026-010401080313-workbench-realtime-authority.md) 定义的 direct publish 组件，把 `agentrun.event.v1` 转换为 `hwlab.event.v1`，不要求内联数据库事务。 |
| WorkbenchReadModel | [PJ2026-010401080313 Workbench实时权威](PJ2026-010401080313-workbench-realtime-authority.md) 定义的唯一读模型，从 durable Workbench facts 组装 session detail、message page、turn snapshot、trace event page 和 session rail summary。 |
| Workbench event stream | `hwlab.event.v1` append-only Kafka event stream；API 只暴露其 revision、cursor 和 replay envelope，不得建立第二 API 事实源。 |
| Serve Session Authority | 会话级 typed event 权威；API mutation 提交执行命令，GET/SSE/compat wrapper 不得建立数据库 outbox authority。 |
| eventSeq/aggregateSeq | Workbench event stream 的全局 cursor 与 aggregate 内 cursor，用于 SSE replay、projection revision、新鲜度和乱序检测；它们与 AgentRun `sourceSeq` 分离。 |
| Derived read cache | WorkbenchReadModel 内部可选的 Redis 派生读缓存；它只缓存 durable projection 派生快照，不改变 API 资源、权限、lifecycle 或 final response 语义。 |
| CacheDiagnostic | Workbench pure-read 响应或 OTel span 中用于解释缓存行为的诊断字段集合，至少包含 `cacheStatus`、`cacheAgeMs`、cacheKeyClass、projectionSeq 或等价字段；它不参与业务状态判断。 |
| cacheStatus | 缓存行为枚举，至少包含 `hit`、`miss`、`stale`、`unavailable`；该字段只能用于性能诊断和 `/performance` 展示，不得用于推断 session/turn/trace lifecycle。 |
| projection commit | projection writer/finalizer 对 message、part、turn、trace 和 session summary 的一次幂等持久化提交；终态 commit 必须保持这些事实语义一致。 |
| projectedSeq | HWLAB durable trace event 的单调分页游标；与 AgentRun `sourceSeq` / `sourceEventId` 分离，用于 Workbench trace page、SSE replay 和 fake-server fixture。 |
| 读侧推理 | API route、read model、SSE consumer、compat wrapper、CLI renderer、Web reducer/selectors、fake-server 或测试断言根据 trace tail、message text、tool event、result cache、session summary、list row、workspace snapshot、localStorage 或 elapsed timeout 推断 lifecycle、terminal、running 或 final response 的行为；API契约禁止该模式。 |

## 4. 系统边界和接口

本规格把 API契约作为客户端方向下的接口语义层看待；本章只描述输入、输出和责任边界。

| 边界项 | 内容 |
| --- | --- |
| 外部使用者 | Cloud Web、HWLAB CLI、自动化脚本、平台管理员和需要同源 API 的浏览器客户端。 |
| 外部输入 | HTTP method、相对 path、JSON body、query、Web session cookie、Authorization header、`traceparent`、sessionId、turnId、traceId 和业务参数；projectId、workspaceId、conversationId 只允许作为显式筛选或兼容 metadata 输入。 |
| 受控资源 | API path、schema、route policy、AuthPrincipal 摘要、错误码、响应摘要和兼容性契约。 |
| 外部输出 | JSON 响应、HTTP status、`traceparent`、`x-hwlab-otel-trace-id`、`x-request-id`、route、actor、业务标识、错误分类、脱敏状态、trace/result 指针、cache diagnostic 和下一步查询提示。 |
| 用户接口 | Cloud Web 同源 API、HWLAB CLI 同源 REST request/client 命令、HTTP API。 |
| 系统边界 | API契约负责接口形态和错误表达；不拥有用户、Agent、硬件、CaseRun、账本或发布事实。 |

## 5. 内部分工与规格索引

| 编号 | 模块或课题 | 规格文档 | 主责边界 | 上游依赖 | 下游支撑 |
| --- | --- | --- | --- | --- | --- |
| PJ2026-01040301 | 同源路由 | 本规格 6.1 | Cloud Web 相对 REST/JSON path、route policy 和绝对 URL 禁止边界 | 平台入口、Web工作台 | HWLAB CLI、Cloud Web |
| PJ2026-01040302 | 鉴权主体 | 本规格 6.2 | Web session、API key、actor 摘要和权限错误表达 | 用户管理 | Web、CLI、业务 API |
| PJ2026-01040303 | Agent接口 | 本规格 6.3 | conversation、session、chat、steer、cancel、result、trace 和 inspect API 形态 | Agent编排、用户管理 | Web工作台、HWLAB CLI |
| PJ2026-01040304 | 资源接口 | 本规格 6.4 | workspace、access、provider、HWPOD node-ops、gateway 和性能摘要入口 | 全部业务 L1 | Web、CLI、admin |
| PJ2026-01040305 | 错误输出 | 本规格 6.5 | JSON 成功/失败响应、错误码、route/status、脱敏和渐进披露 | 全部业务 L1 | 用户、脚本、排障 |
| PJ2026-01040306 | 缓存诊断 | 本规格 6.6 | Workbench pure-read cache diagnostic 字段、脱敏边界和禁止状态仲裁 | Workbench实时权威、Workbench性能 | Web、CLI、Performance 页 |
| PJ2026-01040307 | CaseRun接口 | 本规格 6.7 | CaseRun case/run/status/events/aggregate/cancel REST 资源、短返回和错误语义 | HarnessRL、硬件池、Agent编排 | Web CaseRun、CLI、web-probe |
| PJ2026-01040308 | 项目管理接口 | 本规格 6.8 | 项目管理 REST 资源、MDTODO DTO、Workbench launch intent、link 查询和独立微服务代理边界 | 项目管理、Web工作台、用户管理 | `/projects`、`/projects/mdtodo`、CLI、web-probe |

目标后端模块应至少拆分为 route/handler、schema/contract、read model、persistence/projection、AgentRun adapter、trace event pager、SSE/event publisher 和 compat wrapper。route 文件只负责 HTTP 入口、鉴权桥接和错误映射；schema/contract 只负责类型和字段；read model 只组装查询模型；adapter 只转换外部执行事实；projection 只写 durable facts；compat wrapper 只提供旧 path 到新资源的兼容映射，不拥有第二套业务事实。

### 5.1 Workbench实时权威 API 边界

[PJ2026-010401080313 Workbench实时权威](PJ2026-010401080313-workbench-realtime-authority.md) 集中定义 `agentrun.event.v1 -> hwlab.event.v1 -> live/replay SSE` 的目标架构。API 契约只保留接口边界：SSE 从 Kafka cursor 回放并切到同一 live tail；`GET /v1/workbench/*` 只读非阻塞派生模型；compat wrapper 只能调用正式 mutation 或只读接口。

Workbench mutation API 必须在 AgentRun durable admission 成功后短返回 `202 Accepted`，并包含可继续关联的 `sessionId`、`turnId`、`traceId`、`commandId` 或 no-op reason。`POST /v1/agent/chat`、steer、cancel 和 retry 不得等待 PostgreSQL projection、outbox、migration、runner 启动、result polling 或 trace hydration；派生读模型失败只能形成 warning/diagnostic，不能把已接纳命令改写为 admission failure。

Workbench SSE/event API 必须按 `hwlab.event.v1` Kafka cursor replay。`GET /v1/workbench/events?afterSeq=N` 或等价入口返回的每条 envelope 至少应携带 Kafka cursor、`sessionId`、`turnId`、`traceId`、entity family/id/version、`projectionRevision` 和脱敏 payload 摘要。回放追上水位后必须在同一连接切到 live tail；缺口进入 transport diagnostic 和 Kafka retention replay，禁止 REST 补洞或数据库 outbox replay。

若 AgentRun 事实已经 terminal 但 Workbench projection 尚未追平，GET 响应应暴露 `projectionStatus`、`lastProjectedSeq`、`sourceRunId`、`sourceCommandId`、`blocker` 或等价诊断字段；不得在 GET 中同步调用 AgentRun、Code Agent manager、legacy conversation manager、billing finalizer、workspace repair、trace polling 或 result polling 来推进事实。读模型、SSE publisher、compat wrapper 和 CLI render 都不能各自解释 terminal、running、final response 或 trace pagination；它们只能重放 durable projection。

API 契约严禁读侧推理。`GET /v1/workbench/*`、`/v1/agent/*` compat wrapper、SSE event envelope 和 CLI render 只能返回唯一 Workbench durable projection 中已经存在的 lifecycle/final/status 字段；不得从 `result?.status ?? trace?.status`、最后一条 trace event `completed`、message text 是否为空、part/tool row 状态、session list summary、workspace selected state、轮询耗时或 elapsed timeout 合成 terminal、running、finalResponse 或 `caught-up`。投影缺失或矛盾时返回结构化 unknown/projecting/degraded/blocker，而不是在读侧选择一个优先级最高的事实源。

Derived read cache 只能作为 WorkbenchReadModel 内部性能优化。`GET /v1/workbench/*` 可以返回 `cacheStatus`、`cacheAgeMs`、cacheKeyClass、projectionSeq、dbQueryAvoided 或等价 CacheDiagnostic 字段，用于解释 hit/miss/stale/unavailable 和读路径耗时；这些字段不得影响 HTTP resource identity、AuthPrincipal、owner visibility、lifecycle、terminal、finalResponse、projectionStatus、404/410 判断或 compat wrapper 映射。Redis unavailable 时，API 只能返回 `cacheStatus=unavailable` 加受控 DB read 或明确 degraded，不能让 liveness 失败，也不能切换到 Cloud API/Web 前端 Redis 直连。

### 5.2 Workbench pure-read DTO 字段矩阵

`GET /v1/workbench/*` 的响应 DTO 只表达已经提交的 durable Workbench projection 和独立诊断。API 可以为渐进披露裁剪长字段，但必须保留 authority 边界和可继续查询的稳定标识。

| DTO | 必需身份字段 | lifecycle/final 字段 | diagnostic 字段 | 禁止解释 |
| --- | --- | --- | --- | --- |
| SessionRailItem | `sessionId`, `currentTurnId` 可空 | `status`, `running`, `summary`, `updatedAt` | `projectionStatus`, `blocker` 可空 | 用 list row 推断当前 message final 或 trace terminal。 |
| SessionDetail | `sessionId`, `threadId`/`conversationId` metadata 可空 | `status`, `running`, `currentTurnId`, `messagePageCursor` | `projectionStatus`, `lastProjectedSeq`, `sourceRunId`, `sourceCommandId`, `blocker` | 在 detail GET 内调用 AgentRun、workspace repair 或 result sync 推进事实。 |
| MessagePage | `sessionId`, `messageId`, `partId`, `turnId` | `role`, `status`, `sealedAt`, `parts[].finalResponse` | part/message diagnostic 可空 | 根据空 text、trace tail 或 result cache 合成 assistant final。 |
| TurnSnapshot | `turnId`, `traceId`, `sessionId`, `sourceRunId`, `sourceCommandId` | `status`, `terminal`, `failureKind`, `sealedAt`, `messageIds` | `projectionStatus`, `lastProjectedSeq`, `blocker` | 根据最后一条 trace event、轮询耗时或 transport error 推断 terminal。 |
| TraceEventPage | `traceId`, `range`, `events[].projectedSeq` | trace event `type/status` 和 durable terminal event | `hasMore`, `nextCursor`, `sourceSeq/sourceEventId` 审计字段 | 用 `hasMore=false` 推断 turn terminal；混用 `sourceSeq` 和 `projectedSeq` 作为 cursor。 |
| EventStreamEnvelope | `eventSeq`/`outboxSeq`, `aggregateId`, `aggregateSeq`, `sessionId`, `turnId`, `traceId` | 已投影 commit 的 notification，不直接承载第二套 lifecycle | `projectionRevision`, `commitType`, `replay`, `gap`, `valuesRedacted=true` | 按 SSE 到达顺序重排 timeline；用 event payload 直接覆盖 read model。 |
| ProjectionDiagnostic | `sessionId`, `turnId`, `traceId`, `sourceRunId`, `sourceCommandId` | 无主 timeline 写权 | `projectionStatus`, `health`, `lag`, `lastProjectedSeq`, `sourceLatestSeq`, `blocker`, `updatedAt` | 把 diagnostic 文案覆盖 sealed final response。 |
| TransportDiagnostic | `sessionId`, `turnId`, `traceId` 可空 | 无主 timeline 写权 | SSE、poll、hydration、result sync 或 network health | 把 transport failure 改写成 message/turn terminal 状态。 |

Workbench message/turn DTO 可以输出 timing metadata，但字段权威必须保持单一：`startedAt`、`lastEventAt`、`finishedAt` 和 sealed `durationMs` 来自 Workbench durable projection；running 消息的“最近/耗时”由浏览器以这些时间戳和本地 now 计算；terminal 消息使用 sealed `durationMs` 或等价 sealed `finishedAt - startedAt`，不得继续增长。`lastEventAgeMs` 若在迁移期保留，只能作为服务端观测时刻的快照或诊断字段，不能作为 Cloud Web 用户可见相对时间的显示权威，也不能参与 lifecycle、terminal、projectionStatus、session sorting 或 final response 选择。

Workbench pure-read DTO 可以携带 CacheDiagnostic，但字段必须与业务事实分仓表达。`cacheStatus`、`cacheAgeMs`、`cacheKeyClass`、`projectionSeq`、`dbQueryAvoided`、`dbQueryDuration` 和 `payloadBytes` 只能放在 `diagnostic`、`performance`、`meta` 或等价非 authority 字段下；默认响应不得输出完整 cache key、actor id、session id 以外的派生 key、trace id 组合 key、prompt、assistant 正文、tool 参数、stdout/stderr、Secret、token、cookie、Authorization header 或 DB DSN。客户端可以显示或上报这些诊断用于性能分析，但不得把 `hit` 视为新鲜、把 `stale` 视为 terminal、把 `unavailable` 视为业务失败，或根据 cache miss 触发 read-through repair。

API 禁止路径清单如下：GET read-through repair、session JSON lifecycle 推理、result cache lifecycle 推理、trace tail lifecycle 推理、workspace/conversation authority、billing finalizer side effect、Code Agent manager shortcut、frontend status priority 和 compat wrapper second authority。需要推进 projection 时必须由 writer/finalizer、后台 scheduler、显式 mutation 或受控 replay/reprojection 入口执行；GET、SSE、CLI render 和 fake-server 只能观察结果。

### 5.3 Workbench cache diagnostic 数据流

```mermaid
flowchart LR
  Client[Web / CLI] --> Route[GET /v1/workbench/*]
  Route --> Auth[AuthPrincipal + owner visibility]
  Auth --> Read[WorkbenchReadModel]
  Read --> Cache[(Derived Redis cache)]
  Cache -->|hit with matching authority input| Dto[Pure-read DTO]
  Cache -->|miss / stale / unavailable| Facts[(Durable Workbench facts)]
  Facts --> Read
  Read --> Dto
  Dto --> Diag[CacheDiagnostic meta]
  Diag --> Client
```

缓存诊断数据流要求 route 先完成鉴权和 owner visibility，再由 WorkbenchReadModel 使用 durable facts authority input 访问 Redis 派生缓存。cache hit 只能返回等价 pure-read DTO；cache miss、stale 或 unavailable 必须回到 durable facts read 或明确 degraded，不得触发 AgentRun、result polling、workspace repair、projection finalizer 或前端 Redis 直连。

```mermaid
sequenceDiagram
  participant C as Web/CLI
  participant A as Cloud API route
  participant R as WorkbenchReadModel
  participant K as Redis derived cache
  participant F as Durable facts
  C->>A: GET /v1/workbench/sessions
  A->>R: principal + query + route
  R->>K: get cache key with authority input
  alt hit and revision matches
    K-->>R: derived DTO + cacheAgeMs
  else miss/stale/unavailable
    R->>F: read durable projection
    F-->>R: authoritative DTO or degraded/blocker
    R->>K: set derived snapshot when allowed
  end
  R-->>A: DTO + CacheDiagnostic
  A-->>C: JSON response, lifecycle from durable projection only
```

## 6. 原子需求

### 6.1 CLIENT-API-REQ-001 同源路由

| 编号 | 短名 | 主责模块 | 关联模块 |
| --- | --- | --- | --- |
| CLIENT-API-REQ-001 | 同源路由 | PJ2026-01040301 同源路由 | [PJ2026-010401 Web工作台](PJ2026-010401-web-workbench.md)、[PJ2026-010402 HWLAB CLI](PJ2026-010402-hwlab-cli.md)、[平台运维](PJ2026-0106-platform-ops.md) |

API契约应定义 Cloud Web origin 下的相对 REST/JSON path 和 route policy，使浏览器与 HWLAB CLI 能通过同一同源入口访问业务 API。

同源路由必须拒绝把绝对 URL、内部服务地址或旧运行面端口作为正式用户入口。平台运维提供公开入口和代理支撑；API契约只规定客户端可依赖的 path 和错误形态。

API契约必须区分同源 API path 与浏览器 history route：`/auth*`、`/v1*`、`/health*` 等 API path 返回 JSON 语义，`/workbench`、`/login` 和其他 Cloud Web 页面路由由 Web工作台处理。API/edge 直达入口对页面路由返回 JSON 404 是正确边界，不应被改造成 SPA fallback；反过来，Web正式公开入口必须通过 route policy 把 API path 分流到 API 服务。

RESTful API 必须保持无状态请求语义：在相同 method、path、query、body、auth 主体和相同后端业务事实下，应返回同一语义结果；服务端不得为了补齐某个列表或状态而隐藏读取浏览器 workspace selected、localStorage、Web snapshot、上一次页面状态或其他客户端临时状态。列表窗口需要包含某个当前资源时，必须通过显式 query/path/body 传入稳定业务 id，例如 session list 通过显式 include session id 请求把当前 session 纳入同一列表响应；服务端只能从当前 Web session/API key 恢复 AuthPrincipal，校验该 id 对当前 actor 是否可见并返回后端事实，不得接受客户端拼接出的 project、workspace、status、final response、markdown、running 动效或 stub 作为事实来源。

当前会话消息恢复必须使用 session 主路径：`GET /v1/workbench/sessions/{sessionId}` 返回 session metadata 和消息指针，`GET /v1/workbench/sessions/{sessionId}/messages` 返回 message/part page，turn 和 trace 分别由 `TurnSnapshot` 与 `TraceEventPage` 补洞。`GET /v1/workbench/workspace` 可以返回 selected ids、workspace revision 和 composer 配置，但不得成为消息本体 authority；`GET /v1/workbench/sessions` 可以返回 rail 摘要和显式 include 的列表项，但不得替代当前 session detail/message page。前端恢复、深链、刷新和 session 切换必须以稳定 sessionId 或后端认可 opaque session handle 调用同一 detail/message resource，不得在 detail resource 失败时切到 workspace snapshot、conversation detail、列表摘要、JSON-RPC、内部 manager、浏览器缓存或空 stub 继续构造当前消息面板。

### 6.2 CLIENT-API-REQ-002 鉴权主体

| 编号 | 短名 | 主责模块 | 关联模块 |
| --- | --- | --- | --- |
| CLIENT-API-REQ-002 | 鉴权主体 | PJ2026-01040302 鉴权主体 | [用户管理](PJ2026-0105-user-management.md)、[PJ2026-010402 HWLAB CLI](PJ2026-010402-hwlab-cli.md) |

API契约应以 AuthPrincipal 摘要表达 Web session 和 API key 恢复出的当前用户，使 Web、CLI 和 HTTP API 可以一致处理未登录、无权限、账号禁用、额度不足和工具能力缺失。

鉴权主体的真相由用户管理负责。API 响应只输出 actor、authMethod、requiredAuthMethod、role/status、scope 摘要和错误分类，不输出 session token、完整 API key、password hash、Secret value 或可复用凭据。

`/auth/session` 是 Cloud Web 的最小身份验证端点。浏览器请求只依赖同源 `hwlab_session` cookie transport；前端不得向该端点传入 API key、user-billing token、cookie prefix、localStorage token 或第二套 authority。该端点成功时必须返回 canonical principal，至少包含：

| 字段 | 语义 | 脱敏边界 |
| --- | --- | --- |
| `authenticated` | 当前请求是否已通过认证 | 布尔值 |
| `authMethod` | 认证 transport 来源，例如 `web-session`、`user-billing-session`、`user-billing-api-key`、`local-bootstrap` | 枚举，不输出 token |
| `identityAuthority` | 身份事实归属，例如 `hwlab-local`、`user-billing`、`bootstrap-recovery` | 枚举，不输出底层凭据 |
| `sessionKind` | 调用形态，例如 `browser`、`api-key`、`recovery` | 枚举 |
| `actor` | 当前用户摘要，至少包含稳定 id、role、status；可包含 username/displayName | 不输出 password hash、session secret、完整邮箱验证 payload |
| `capabilities` | 后端授权摘要，例如 `web`、`workbench`、`codeAgent`、`billing`、`admin` | 只输出 `available`、`forbidden`、`unlinked`、`unavailable` 或等价短枚举 |
| `valuesRedacted` | 响应是否遵守脱敏边界 | 必须为 true |

未登录请求必须返回 401 和结构化 unauthenticated 结果；禁用、撤销或 recovery 能力不足必须返回 403 或业务 blocker，并保留 `authMethod`、`identityAuthority`、`sessionKind` 的可诊断摘要。响应不得输出 cookie、session token、API key secret、完整 user-billing token、provider Secret、DB DSN 或 raw dependency payload。

业务 route 只能消费认证层产生的 canonical principal 和权限配额的 capability 结果。Usage、Billing、API keys、Workbench、Code Agent 和 Admin route 不得自行调用 token/cookie 抽取函数来决定用户身份；如果需要访问 user-billing、API key 或账单后台，应由后端以 principal 为输入桥接业务服务，并把 `available/forbidden/unlinked/unavailable` 或结构化 blocker 返回给客户端。

### 6.3 CLIENT-API-REQ-003 Agent 用户接口

| 编号 | 短名 | 主责模块 | 关联模块 |
| --- | --- | --- | --- |
| CLIENT-API-REQ-003 | Agent接口 | PJ2026-01040303 Agent接口 | [Agent编排](PJ2026-0102-agent-orchestration.md)、[用户管理](PJ2026-0105-user-management.md)、[PJ2026-010401 Web工作台](PJ2026-010401-web-workbench.md) |

API契约应定义显式 Agent session、turn、chat、steer、cancel、result、trace 和 inspect 的用户可见 RESTful HTTP 形态，使 Web 和 HWLAB CLI 能围绕同一 sessionId、turnId、traceId、runId 和 commandId 操作。conversationId、threadId 可以作为后端返回的 metadata 或兼容映射字段存在，但不得替代 sessionId 成为 Web deep link 或 active authority。

Agent 接口不得通过 session latest、浏览器历史、旧 cache 或内部 manager 生成第二套 final response。Agent编排提供执行事实，用户管理校验 owner 和权限，客户端只稳定暴露用户入口。

Trace 查询必须采用无状态 RESTful 资源形态：`GET /v1/agent/traces/{traceId}` 是客户端读取 Code Agent trace events、runnerTrace、terminalEvidence、finalResponse 和 traceSummary 的权威入口。请求只依赖 method、path、query、auth 主体和后端业务事实；Web 不得使用动作式 `/v1/agent/chat/trace/{traceId}` 作为恢复会话、切换 session 或 completed 消息加载 trace 的前端路径，也不得把浏览器 workspace snapshot、localStorage 或消息 stub 拼成 trace 响应。

Trace resource 必须支持按事件位置继续读取，例如 `sinceSeq` / `cursor` 与 `limit` 这类 query 参数。服务端返回超长 trace 时应按完整事件边界截断，并在同一个资源响应中给出 `range`、`hasMore`、`nextSinceSeq` 或等价阶段性位置，使客户端可以继续请求下一段；响应不得返回半条事件，不得要求 JSON-RPC、回放按钮、内部 manager path 或第二套 trace endpoint 才能补齐完整可读事件。分页请求必须保持无状态和幂等：同一 `traceId`、同一位置参数、同一权限主体下重复请求不得污染 trace 事实。

`POST /v1/agent/chat`、`GET /v1/agent/turns/{traceId}` 和 result 入口可以返回 `traceUrl` 指针，但该指针必须指向 RESTful trace resource。trace resource 可以为了短返回默认裁剪事件，但完整事件补齐、终态 final response、AgentRun run/command identity、错误和脱敏边界仍由同一 trace resource 表达；客户端不得要求用户手动“回放 Trace”才能获得完成态 trace。

目标 Workbench RESTful API 资源设计如下。旧 `/v1/agent/*` 路径可以作为兼容 wrapper 暂存，但 Web 主路径应逐步收敛到这些资源语义。

| 资源 | Method / path | 请求字段 | 响应主体 | 状态码 | 权威边界 |
| --- | --- | --- | --- | --- | --- |
| WorkspaceSelection | `GET /v1/workbench/workspace` | `projectId`, `workspaceId` 可选 | selected ids、workspace revision、composer 配置、public origin 摘要 | 200/401/403 | 只读 selection/config，不返回或修补 message 事实 |
| WorkspaceSelection | `PATCH /v1/workbench/workspace` | 显式 selected project/workspace/conversation ids | 更新后的 selection/config | 200/400/401/403 | 只有显式 mutation 可改变 selection |
| Session list | `GET /v1/workbench/sessions` | `cursor`, `limit`, `includeSessionId`，可选后端认可筛选字段 | session/conversation rail 摘要 page | 200/401/403 | 当前用户可见 session 列表和排序，不替代 detail |
| Session detail | `GET /v1/workbench/sessions/{sessionId}` | path sessionId | session metadata、message page 指针和当前 turn/trace 摘要 | 200/401/403/404 | session metadata，不拥有 trace events |
| Message page | `GET /v1/workbench/sessions/{sessionId}/messages` | `cursor`, `limit`, `before/after` | message + parts page、next cursor | 200/401/403/404 | 会话 timeline 的 message/part 事实 |
| Conversation detail | `GET /v1/agent/conversations/{conversationId}` | path conversationId | conversation metadata、message page seed、sessionId、threadId、lastTraceId、turn summary | 200/401/403/404 | 迁移期兼容 detail，不替代 session 主路径 |
| Turn admission | `POST /v1/workbench/sessions/{sessionId}/turns` | prompt/parts、delivery、client message ids、可选 metadata | `202 Accepted` + turnId、traceId、userMessageId、assistantMessageId、status URLs | 202/400/401/403/409/422 | 只表示输入已接收，不表示执行完成 |
| Turn snapshot | `GET /v1/workbench/turns/{turnId}` | path turnId | status、terminal、message ids、traceId、AgentRun refs、failureKind | 200/401/403/404 | turn 状态补洞，不承载 trace events |
| Trace event page | `GET /v1/workbench/traces/{traceId}/events` | `afterSeq/cursor`, `limit` | ordered events、range、hasMore、next cursor | 200/401/403/404/410 | trace 明细分页，不决定主 timeline final response |
| EventStream | `GET /v1/workbench/events` | optional sessionId/turnId/traceId filters | SSE events + heartbeat | 200/401/403 | 实时加速和通知，不替代 REST snapshot |
| Cancel turn | `POST /v1/workbench/turns/{turnId}/cancel` | reason 可选 | updated TurnSnapshot | 200/202/401/403/404/409 | 精确取消目标 turn |
| Steer turn | `POST /v1/workbench/turns/{turnId}/steer` | text/parts、delivery | accepted steer TurnSnapshot 或 continuation id | 202/400/401/403/404/409 | 绑定明确 running turn |
| ProviderProfile | `GET /v1/workbench/provider-profiles` | project/workspace 可选 | profile summary list | 200/401/403 | 配置摘要，不输出 Secret |
| Diagnostic resource | `GET /v1/workbench/diagnostics/{resource}` | resource id/query | redacted health/status summary | 200/401/403/404 | 诊断可见性，不改变业务事实 |

所有 GET path 必须无业务副作用。需要 repair、finalize、sync 或 migration 时，应由后台 finalizer、显式 mutation 或持久化投影入口执行，不能隐藏在 workspace、conversation、turn 或 trace GET 中。所有分页响应必须包含稳定 cursor 或 seq 边界；trace event page 的分页 cursor 使用 HWLAB `projectedSeq`，并保留 AgentRun `sourceSeq/sourceEventId` 作为来源审计字段，二者不得重叠或互相替代。所有错误响应必须包含 route、status、code/blocker、redacted actor、target resource 和下一步建议。

`GET /v1/workbench/sessions/{sessionId}/messages` 返回的 MessagePage 必须是会话 timeline 顺序，而不是数据库写入批次顺序。排序键应优先体现 turn timeline/aggregate seq/事件时间；同一 turn 内用户 message 先于 assistant/agent terminal message；同一 session 多轮刷新后不得出现由 read model 排序造成的 role cluster。Message DTO 应保留 `projectedSeq/sourceSeq/sourceEventId` 供审计，但客户端不得把这些来源字段当作唯一排序修复点。

`GET /v1/workbench/traces/{traceId}/events` 的可见性必须以 trace 所属 turn/message/session durable facts 和当前 actor visibility 判断。历史 trace 不再是 session 当前 `lastTraceId` 时仍应可读；缺页或投影滞后应返回 projection blocker/degraded，而不是 `workbench_trace_not_found` 的 actor 不可见误导。

`GET /v1/workbench/traces/{traceId}/events` 的 continuation 空窗口属于正常分页状态或投影追平窗口，不得返回 404。若 actor 可见且 metadata/checkpoint 表明 trace 仍有未追平区间，API 应返回 `200`、`events=[]`、空 `range.fromProjectedSeq/toProjectedSeq`、原 `nextProjectedSeq`、`status=projecting` 或等价 diagnostic，并暴露 `projectionStatus`、`projectionHealth`、`lastProjectedSeq`、`traceLastSeq`、`fullTraceLoaded=false`、`diagnostic.code=workbench_trace_events_missing`。`404` 只用于真实不可见、未归档、删除或 actor 权限不匹配的 trace，不用于 no-new-events、lag 或 catch-up。

Cancel mutation 在转发 AgentRun cancel 前必须核验本地 sealed terminal 或上游 run/command state。若 command 已 terminal，API 应返回 already-terminal/no-op 或触发 terminal projection，不得把 completed/failed/blocked 等真实 terminal 改写为 canceled。Workbench read paths 仍保持纯读，不在 GET 中执行该核验或修复。

Workbench read-model dependency timeout 必须返回结构化 degraded/readiness/blocker 响应；PostgreSQL pool connect/query timeout 不得作为未捕获异常杀死 cloud-api 进程，也不得让 liveness 因单次 Workbench read query 变成 EOF。

新增或重构的核心后端文件头部必须标注遵循的 SPEC 编号、短名和实现引用版本，例如 `SPEC: PJ2026-010401080313 Workbench实时权威 draft-2026-07-14-p0-pure-kafka-authority; PJ2026-010403 API契约 draft-2026-06-20-p0-workbench-pure-read-api; PJ2026-010205 HWLAB接入 draft-2026-06-17-r0`，并简述文件职责。实现文件不得只写 issue 编号、`latest` 或 `current` 作为规格引用。

### 6.4 CLIENT-API-REQ-004 资源与管理接口

| 编号 | 短名 | 主责模块 | 关联模块 |
| --- | --- | --- | --- |
| CLIENT-API-REQ-004 | 资源接口 | PJ2026-01040304 资源接口 | [硬件池](PJ2026-0101-hardware-pool.md)、[用户管理](PJ2026-0105-user-management.md)、[Agent编排](PJ2026-0102-agent-orchestration.md)、[平台运维](PJ2026-0106-platform-ops.md) |

API契约应定义 workbench workspace、Admin Access、provider profile、HWPOD node-ops、gateway 诊断和性能摘要等资源与管理入口，使 Web 和 HWLAB CLI 可以一致读取状态、提交操作和展示错误。

资源与管理接口只定义调用形态和字段稳定性。HWPOD node-ops 的硬件语义归硬件池，provider profile runtime 归 Agent编排，权限和账号归用户管理，运行健康和公开入口归平台运维。

Gateway 相关 API 只允许作为云端同源入口暴露：用户、Agent 或 smoke 调用 Cloud API / API契约中的 gateway/HWPOD path，由 HWPOD 服务再路由到主动出站的 AI 网关。API契约不得要求客户端直接访问用户 PC gateway，也不得把 gateway 本地端口、内网地址或临时 poll URL 写入正式客户端输出。

### 6.5 CLIENT-API-REQ-005 错误与输出合同

| 编号 | 短名 | 主责模块 | 关联模块 |
| --- | --- | --- | --- |
| CLIENT-API-REQ-005 | 错误输出 | PJ2026-01040305 错误输出 | [PJ2026-010402 HWLAB CLI](PJ2026-010402-hwlab-cli.md)、[PJ2026-010401 Web工作台](PJ2026-010401-web-workbench.md)、[HarnessRL](PJ2026-0103-harness-rl.md) |

API契约应保证成功和失败都以可定位 JSON 形式表达，至少包含调用 route、HTTP status、错误码或 blocker、必要业务标识、脱敏 actor 或权限摘要，以及可继续查询的 result/trace/status 入口。

默认响应不得输出爆炸或泄漏敏感值；需要完整 trace、原始响应或长日志时，必须通过显式展开参数、下载入口或已有业务产物获取。错误输出只负责说明失败，不替代缺失能力实现。

Cloud API 请求入口必须统一 extract 或 create W3C `traceparent`，并把 OTel trace id、span id、request id、route 和 serviceId 绑定到同一次 request context。所有 200、4xx 和 5xx 响应都必须带 `traceparent`、`x-hwlab-otel-trace-id` 和 `x-request-id`；业务 handler、dependency client、dev-entrypoint proxy、AgentRun adapter、user-billing、HWPOD 或其他下游调用只能传播该 context，不能在同一请求内随机生成第二个用户可见 trace id。

用户可见 API 失败响应应统一使用 `HwlabErrorEnvelope` 或等价结构：

```ts
interface HwlabErrorEnvelope {
  ok: false;
  status: number | "failed";
  error: {
    code: string;
    message: string;
    userMessage?: string;
    category?: string;
    layer?: string;
    retryable?: boolean;
    diagnostic: {
      traceId: string | null;
      spanId?: string | null;
      requestId?: string | null;
      route?: string | null;
      serviceId?: string | null;
      observedAt: string;
      valuesRedacted: true;
    };
  };
  valuesRedacted: true;
}
```

`error.message` 和 `error.userMessage` 只承载用户可理解文案；`error.diagnostic.traceId` 承载可复制的 OTel trace id。响应不得向用户外显 `tracestate`、`baggage`、cookie、API key、完整 Authorization header、DB DSN、provider token、prompt、assistant 正文、tool 参数、stdout/stderr 长段、raw upstream payload 或可复用凭据。`traceparent` 可作为标准 header 返回，但默认页面只展示 `trace_id` 字段。

错误 envelope 的结构化日志必须记录同一 `trace_id`、`request_id`、`error.code`、route、status 和 layer，使日志、span 和用户错误诊断能关联。`trace_id` 不得作为 Prometheus label；如需 metrics 关联，只能通过低基数 route/status/code label、exemplar 或受控 debug payload 承载。

Workbench 投影 blocker、proxy timeout、鉴权失败、provider profile、web-performance、全局 route exception 和 dev-entrypoint proxy 错误都应映射到同一 envelope。历史 `{ error: string }` 字段可在迁移期作为 `userMessage` 展示兼容，但不得成为唯一错误事实；新增用户可见错误路径禁止只返回裸字符串、`reason` 或未脱敏 raw exception。

### 6.6 CLIENT-API-REQ-006 Workbench cache diagnostic 字段

| 编号 | 短名 | 主责模块 | 关联模块 |
| --- | --- | --- | --- |
| CLIENT-API-REQ-006 | 缓存诊断 | PJ2026-01040306 缓存诊断 | [Workbench实时权威](PJ2026-010401080313-workbench-realtime-authority.md)、[Workbench性能](PJ2026-01060505-workbench-performance.md) |

Workbench pure-read API 可以在成功或 degraded 响应中输出 CacheDiagnostic，用于说明 Redis 派生读缓存是否命中、是否 stale、是否 unavailable、快照年龄和是否避免 durable DB 查询。该诊断至少应能表达 `cacheStatus=hit|miss|stale|unavailable`、`cacheAgeMs`、cacheKeyClass、projectionSeq 或等价 revision、dbQueryAvoided、dbQueryDuration 和 payloadBytes；字段位置必须与 lifecycle/final/status 分仓。

CacheDiagnostic 不得成为业务状态仲裁字段。Web、CLI、compat wrapper、fake-server 和 web-probe 不得根据 `cacheStatus`、`cacheAgeMs`、Redis key presence 或 dbQueryAvoided 推断 session running/completed、turn terminal、trace caught-up、final response、deleted/archived/not-found 或权限可见性。缓存 unavailable 不应触发 `/health/live` 失败；route 可以返回受控 DB read 结果或明确 degraded/blocker，但不能静默切换到 Cloud API/Web 前端 Redis 直连或旧 fallback path。

CacheDiagnostic 必须脱敏并保持低基数。响应、日志、OTel span 和 `/performance` 默认展示不得包含完整 cache key、actor id 聚合维度、trace id 组合 key、prompt、assistant 正文、tool 参数、stdout/stderr、Secret、token、cookie、Authorization header、DB DSN 或 raw upstream payload。需要排查单次请求时，只允许通过 OTel trace id、request id、route、cacheKeyClass、projectionSeq 和 valuesRedacted 摘要关联。

### 6.7 CLIENT-API-REQ-007 CaseRun 用户接口

| 编号 | 短名 | 主责模块 | 关联模块 |
| --- | --- | --- | --- |
| CLIENT-API-REQ-007 | CaseRun接口 | PJ2026-01040307 CaseRun接口 | [HarnessRL](PJ2026-0103-harness-rl.md)、[硬件池](PJ2026-0101-hardware-pool.md)、[Agent编排](PJ2026-0102-agent-orchestration.md)、[PJ2026-010401 Web工作台](PJ2026-010401-web-workbench.md) |

API契约应定义 YAML-selected Cloud Web origin 下的 CaseRun RESTful 资源，使 Web、HWLAB CLI 和 web-probe 能通过同一用户身份、同一相对 path 和同一错误 envelope 提交、查询、取消和阅读 CaseRun。origin 是 public 还是 internal 由 node/lane YAML 和客户端端点解析决定，API 契约只要求同源 path 与错误语义一致。

目标 CaseRun API 资源设计如下。具体字段可随实现演进，但资源身份、短返回、业务事实归属和禁止后门路径必须保持稳定。

| 资源 | Method / path | 请求字段 | 响应主体 | 状态码 | 权威边界 |
| --- | --- | --- | --- | --- | --- |
| Case list | `GET /v1/caserun/cases` | 可选 `hwpodId`、`target`、`capability` 筛选 | caseId、描述、目标硬件、stage 模板、所需 HWPOD capability | 200/401/403 | 只列出 HarnessRL 可运行 case，不证明资源当前可用 |
| HWPOD readiness summary | `GET /v1/caserun/hwpods` 或复用正式 HWPOD spec/readiness 资源 | 可选 caseId | hwpodId、nodeId、capabilities、availability、blocker 摘要 | 200/401/403 | 硬件事实来自硬件池，CaseRun API 只交接摘要 |
| Run admission | `POST /v1/caserun/runs` | caseId、hwpodId、providerProfile、subjectRef、可选 metadata | `202 Accepted`、runId、statusUrl、eventsUrl、aggregateUrl、traceUrl 可空 | 202/400/401/403/409/422 | 只表示提交被接收，不表示 Agent 或硬件动作完成 |
| Run list | `GET /v1/caserun/runs` | 可选 status、limit、cursor | 按 createdAt 倒序的 runId、caseId、workflow identity、status、stage、terminal、时间与证据摘要，以及 nextCursor | 200/400/401/403 | 共享 HarnessRL registry 的纯读发现入口；不扫描 CLI 本地文件，不替代详情、事件或 aggregate |
| Run status | `GET /v1/caserun/runs/{runId}` | path runId | stage、terminal、caseId、hwpodId、nodeId、traceId、AgentRun refs、HWPOD operation summaries、latest blocker | 200/401/403/404 | HarnessRL read model，不直接同步推进 AgentRun 或 HWPOD |
| Run events | `GET /v1/caserun/runs/{runId}/events` | cursor/afterSeq、limit | stage/event page、next cursor、valuesRedacted | 200/401/403/404 | 事件阅读，不拥有第二套 run 状态 |
| Run aggregate | `GET /v1/caserun/runs/{runId}/aggregate` | 可选 detail level | aggregate、artifact manifest hash、evidence refs、replay refs、diagnostic | 200/401/403/404/409 | 低噪声结果入口，评价语义归 HarnessRL |
| Run artifact manifest | `GET /v1/caserun/runs/{runId}/artifacts` | 可选 artifact type | manifest、artifact refs、hash、size、redaction 摘要 | 200/401/403/404 | 只返回索引和允许下载的受控摘要 |
| Run cancel | `POST /v1/caserun/runs/{runId}/cancel` | reason 可选 | updated Run status 或 accepted cancel refs | 200/202/401/403/404/409 | 精确取消目标 run，不根据最新 run 或页面状态推断 |

CaseRun API 的 GET path 必须纯读。投影滞后、AgentRun 未终态、HWPOD node 离线、operation result 缺失、aggregate 未生成或 artifact store 不可达时，GET 应返回 projecting、degraded、blocked 或结构化 failure；不得在 GET 中同步触发 AgentRun dispatch、HWPOD 写操作、workspace repair、result polling finalizer 或旧 CLI 回放。

CaseRun API 必须把事实来源分仓输出。AgentRun refs 至少应能指向 runId/commandId/traceId 或等价执行标识；HWPOD refs 至少应能指向 hwpodId、nodeId、leaseId 或 operation result id；HarnessRL refs 至少应能指向 CaseRun runId、stage revision、artifact manifest hash 和 aggregate revision。API 不得把 stdout 尾部、客户端轮询耗时、trace tail、Web DOM、web-probe 报告或人工 issue 评论作为 run terminal 或 pass/fail 事实。

web-probe 使用 CaseRun API 时必须保持 YAML-selected origin 下的同源用户路径。它可以通过页面控件或同源正式 mutation 提交 run，并通过同一 status/events/aggregate 资源观察终态；当 YAML 选择 internal origin 时可以使用内部 IP，但仍不得调用未声明的内部 service URL、Kubernetes、Postgres、D601 SSH、本地 gateway 端口、旧 v0.2 runner 或测试 fixture 来补齐 CaseRun 成功证据。

### 6.8 CLIENT-API-REQ-008 项目管理用户接口

| 编号 | 短名 | 主责模块 | 关联模块 |
| --- | --- | --- | --- |
| CLIENT-API-REQ-008 | 项目管理接口 | PJ2026-01040308 项目管理接口 | [PJ2026-010404 项目管理](PJ2026-010404-project-management.md)、[PJ2026-010401 Web工作台](PJ2026-010401-web-workbench.md)、[用户管理](PJ2026-0105-user-management.md) |

API契约应定义 YAML-selected Cloud Web origin 下的项目管理 RESTful 资源，使 Cloud Web、HWLAB CLI 和 web-probe 能用同一用户身份、同一相对 path、同一错误 envelope 和同一脱敏边界访问项目、source、MDTODO 文件、任务、Workbench link 和 Workbench launch intent。

目标项目管理 API 资源设计如下。Cloud API 可以作为同源 route/auth bridge 转发到独立 `hwlab-project-management` 微服务，但不得拥有项目管理持久化、Markdown 解析、外部 PM sync、Workbench link 表或 adapter 状态。

Cloud API 的 Project Management proxy 必须按同源鉴权和脱敏 envelope 转发下表所有项目管理资源所需的 `GET`、`HEAD`、`POST`、`PATCH` 和 `DELETE` 方法；只允许 `POST /v1/project-management/workbench-links` 这类单点写入的代理白名单是不合规的。代理层不得把 MDTODO source create/update/probe/reindex、file content 写入、task mutation/create/delete 拦截成 404/405，也不得要求 Cloud Web、web-probe 或 CLI 改用私有 service URL 绕过同源入口。

| 资源 | Method / path | 请求字段 | 响应主体 | 状态码 | 权威边界 |
| --- | --- | --- | --- | --- | --- |
| Navigation | `GET /v1/project-management/navigation` | 可选 `projectId` | 导航可见性、capability、默认 project/source 摘要、blocker | 200/401/403 | 只表达项目入口能力，不返回 raw Markdown |
| Project list | `GET /v1/project-management/projects` | cursor、limit、可选筛选 | `projectId`、title、source 摘要、visibility、updatedAt | 200/401/403 | 当前 actor 可见项目列表 |
| MDTODO sources | `GET /v1/project-management/mdtodo/sources` | 可选 projectId | `sourceId`、displayName、sourceKind、root 摘要、read/write capability、parse/index status | 200/401/403 | 不暴露 host path、Secret 或完整 Git remote |
| MDTODO source create | `POST /v1/project-management/mdtodo/sources` | `sourceKind=hwpod-workspace`、`hwpodId`、`nodeId`、`mdtodoRootRef`、capability | Source DTO、source revision、probe status 可空 | 201/400/401/403/409/422 | 只创建受控 Source 配置，不直接暴露 HWPOD 文件系统 |
| MDTODO source update | `PATCH /v1/project-management/mdtodo/sources/{sourceId}` | displayName、root/capability patch、expected revision | updated Source DTO | 200/400/401/403/404/409/422 | 更新 ProjectSource registry，不改写任务文件 |
| MDTODO source delete | `DELETE /v1/project-management/mdtodo/sources/{sourceId}` | expected source revision、confirm impact | deleted sourceId、affected file/task/link counts、audit id | 200/400/401/403/404/409/422 | 禁用或删除 Source 配置，不删除 HWPOD workspace 文件 |
| MDTODO source probe | `POST /v1/project-management/mdtodo/sources/{sourceId}/probe` | 可选 probe options | HWPOD workspace op 摘要、capability、blocker、timing | 200/202/400/401/403/404/409/422 | 只探测连接和权限，不写任务事实 |
| MDTODO source reindex | `POST /v1/project-management/mdtodo/sources/{sourceId}/reindex` | expected source revision 可选 | projection status、indexed files/tasks、diagnostics | 200/202/400/401/403/404/409/422 | 从 Markdown 重建 projection，不把 DB 反写为权威 |
| MDTODO files | `GET /v1/project-management/mdtodo/files` | `sourceId`、cursor、limit | fileRef、displayName、taskCount、status、fingerprint hash、parseError 摘要 | 200/401/403/404 | 文件摘要，不返回完整 Markdown |
| MDTODO file content | `GET /v1/project-management/mdtodo/files/{fileRef}/content` | `sourceId`、可选 redaction mode | 脱敏 Markdown preview 或 rawContent capability、revision、sha256、parser diagnostics | 200/401/403/404/409/422 | 默认不返回完整 raw Markdown；完整内容必须显式 capability |
| MDTODO tasks | `GET /v1/project-management/mdtodo/tasks` | `sourceId`、可选 fileRef、parentTaskRef、status/filter、cursor/offset、limit | bounded `ProjectTask[]`、taskRef、父子关系、linkCount、projection revision、page/limit/total/hasMore | 200/401/403/404/409/422 | 任务投影来自项目管理 read model；不得默认返回 source 全量任务 |
| MDTODO task mutation | `PATCH /v1/project-management/mdtodo/tasks/{taskRef}` | title/body/raw block/status/metadata patch、expected revision | updated ProjectTask、document revision、audit id | 200/400/401/403/404/409/422 | 写入必须经 MDTODO adapter 文件锁和原子写入 |
| MDTODO task create | `POST /v1/project-management/mdtodo/tasks` | sourceId、fileRef、mode=root/subtask/continue、parent taskRef 可选、title/body、expected revision | created ProjectTask、document revision、audit id | 201/400/401/403/404/409/422 | 生成 Rxx id 并写入 Markdown source-of-truth |
| MDTODO task delete | `DELETE /v1/project-management/mdtodo/tasks/{taskRef}` | expected revision、confirm impact | deleted taskRef、affected refs、document revision、audit id | 200/400/401/403/404/409/422 | 删除 Markdown task block，必须有影响范围摘要 |
| Workbench launch | `POST /v1/workbench/launches` | `WorkbenchLaunchIntent`，含 `sourceKind=project-management`、`projectId`、`taskRef`、prompt template | `202 Accepted` 或 200、sessionId、workbenchUrl、linkId、traceId 可空 | 200/202/400/401/403/404/409/422 | Workbench 公共 mutation，不属于 MDTODO 私有 helper |
| Workbench links | `GET /v1/project-management/workbench-links` | `taskRef`、可选 projectId/sourceId、cursor | linkId、sessionId、traceId、createdBy 摘要、createdAt、role、status | 200/401/403/404 | 只返回 ID 指针和脱敏摘要 |

`ProjectSource` DTO 对 HWPOD-bound source 至少应包含 `sourceId`、`projectId`、`sourceKind="hwpod-workspace"`、displayName、`hwpodId`、`nodeId`、`mdtodoRootRef`、read/write capability、probe status、source revision、index status、blocker 可空和 `valuesRedacted=true`。`mdtodoRootRef` 只能是 workspace 相对根，例如 `docs/MDTODO/`；响应不得返回 host absolute path、Secret、session token、完整 file system listing 或未脱敏 node payload。

`ProjectTask` DTO 至少应包含 `taskRef`、`projectId`、`sourceId`、`fileRef`、`kind`、`rxxId`、title、status、parentTaskRef、depth、linkCount、document revision、updatedAt 和 `valuesRedacted=true`。Rxx 树的 parentTaskRef 来自 `rxxId` 前缀；fileRef、lineNumber、source fingerprint、Markdown preview 和 parse diagnostic 只能作为脱敏 metadata 输出；默认响应不得包含 host path、raw Markdown、Secret、token、DB DSN、provider payload、完整 prompt 或可复用凭据。

`GET /v1/project-management/mdtodo/tasks` 必须是有界读取。未指定 `limit` 时服务端应使用受控默认窗口，指定 `limit` 时必须套用服务端最大值；响应应返回 `total`、`hasMore`、`limit` 和 cursor/offset 摘要。页面首屏应优先读取 source/files 摘要和 selected file 或 selected subtree 的任务窗口；对 `71-FREQ` 这类包含数百个任务的大 source，不得通过一次 source 级全量任务响应支撑默认渲染或 web-probe 验收。

MDTODO mutation 请求必须携带 expected document revision、fingerprint 或等价并发条件。服务端发现 revision 不匹配时返回 409，并输出 current revision、conflict code 和下一步建议；不得用读侧修复、last write wins 或前端重试覆盖远端 Markdown 修改。

`WorkbenchLaunchIntent` 至少应包含 `sourceKind="project-management"`、`projectId`、`taskRef`、title 摘要、promptTemplateId、optional initial prompt metadata 和 `valuesRedacted=true`。Workbench 可以保存这些字段为 session metadata，但不得反解 taskRef、读取 Markdown、调用 Project Management adapter 或把项目管理 DB 当作 Workbench authority。

项目管理 GET path 必须纯读。投影滞后、source 不可用、Markdown parse error、adapter unavailable、link 投影未收敛或 Workbench launch 尚未写回 link 时，GET 应返回 projecting、degraded、blocked 或结构化 failure；不得在 GET 中同步修复文件、触发 Workbench session 创建、调用 AgentRun dispatch、写 DB link、读取运行面 Secret 或访问未声明内部服务。

项目管理 API 的错误必须使用 `HwlabErrorEnvelope` 或等价结构，并携带 route、status、code/blocker、resource id 摘要、OTel trace id、request id、serviceId 和 `valuesRedacted=true`。`mdtodo-raw-markdown-leak`、`mdtodo-hostpath-leak`、`mdtodo-taskref-missing`、`mdtodo-source-probe-failed`、`mdtodo-path-out-of-scope`、`mdtodo-document-revision-conflict`、`mdtodo-rxx-parse-error`、`mdtodo-hwpod-node-unavailable`、`workbench-link-projection-missing` 等应作为可分析 code/blocker，而不是只返回裸字符串。

web-probe observe/analyze 使用项目管理 API 时必须保持同源用户路径。它可以通过页面控件或同源正式 mutation 触发 Workbench launch，并通过自然请求、Resource Timing、DOM 摘要和 link API 观察结果；不得调用私有 service URL、Kubernetes、Postgres、D601 SSH、localStorage 后门或测试 fixture 来补齐通过证据。

## 7. 过程控制

本规格不单独索引一般过程 issue；跨 L1 的内测、灰度和阶段活动索引统一保留在 [PJ2026-01 HWLAB 总规格](PJ2026-01-HWLAB.md) 的 `7. 过程控制`。Workbench Redis 派生读缓存架构执行 issue 为 [#1870](https://github.com/pikasTech/HWLAB/issues/1870)，P0 SPEC-first 子 issue 为 [#1871](https://github.com/pikasTech/HWLAB/issues/1871)，后续实现必须引用 `draft-2026-06-22-p1-workbench-redis-derived-cache`。
