Files
pikasTech-HWLAB/docs/reference/spec-device-pod.md
T
2026-06-01 08:26:21 +08:00

441 lines
24 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Device Pod 正式接入规格
本文是 HWLAB `v0.2` 正式接入 `device-pod` 的规格说明。`device-pod` 是一个逻辑设备能力单元,不是 Kubernetes Pod 名称,也不是 code agent 本地 profile 文件。正式接入后,profile 定义 `device-pod`,因此 profile 必须由管理员和服务端权威存储管理,不能由 code agent 本地文件决定路由或资源边界。
实施跟踪见 [pikasTech/HWLAB#533](https://github.com/pikasTech/HWLAB/issues/533),原 `docs/plan/v02-device-pod-spec-migration.md` 和旧 device-pod MVP 计划全文已迁入该 issue 评论。
旧的 `device-pod-cli` 本地 profile 闭环只用于 CLI MVP 和真实硬件最小验证。进入正式多用户系统后,所有用户态设备访问必须收敛到:
```text
device-pod-cli or cloud-web
-> cloud-api auth + device_pod_grants + lease
-> hwlab-device-pod internal REST
-> gateway transport
-> device-host-cli
-> Keil / pyOCD / UART / target
```
## 在系统中的职责划分
`device-pod` 是云端可授权、可租约保护、可审计的逻辑设备能力单元。`hwlab-cloud-api` 是用户身份、grant、lease、profile authority 和用户态 REST API 的入口;`hwlab-device-pod` 是内部执行服务;`hwlab-gateway` 只承载 transport`device-host-cli` 只在硬件 host 侧执行 Keil、pyOCD、UART 和 workspace 操作。
普通用户、浏览器和 Code Agent session 不直接持有 gateway route、host workspace route、Kubernetes Service 直连能力或 profile 修改权。
## 设计目标
- 用最少组件把 `device-pod-cli` 从“本地 profile + RPC/gateway 调用”迁到“1:1 REST 请求”。
- `cloud-api` 是用户身份、device grant、profile authority 和 lease 判断入口。
- `hwlab-device-pod` 承接设备业务:profile 校验后的运行、job 生命周期、freshness、blocker、bounded output 和 gateway 调用。
- `device-pod-cli` 只做 selector 解析、cloud-api REST 请求和 JSON 输出;默认正式模式不读取 `.device-pod/*.json`,不保存、不上传、不修改权威 profile。
- 第一阶段只部署一个 `hwlab-device-pod` Deployment/Service,管理多个逻辑 `devicePodId`,避免为每台设备创建独立 k8s Service/Deployment。
- 普通用户和 code agent session 不获得 Kubernetes 用户、Service 直连权限、gateway route 或 host workspace route。
## 逻辑模型
一个 `device-pod` 由四个设备能力要素组成:
```text
device-pod
= deviceTarget
+ debugInterface
+ projectWorkspace
+ ioInterface
```
- `deviceTarget`:被测设备目标,例如开发板、用户 PCB 或仪器模块。
- `debugInterface`:下载、复位、chip-id、probe 状态和调试连接能力。
- `projectWorkspace`:源码、工程、构建工具链和 artifact 边界。
- `ioInterface`:UART、DI/DO、采样、日志和其他设备 I/O 观测/控制能力。
`devicePodId` 是云端和用户界面的稳定身份。实际 k8s Pod 可以重建、滚动或扩容;用户和 code agent 不依赖实际 Pod name。
## Profile Authority
正式接入后,profile 是管理员侧资源:
```text
admin UI/API
-> cloud-api
-> device_pods.profile_json + profile_hash
-> hwlab-device-pod internal execution
```
code agent 本地文件只能作为非权威 hint/cache,最多包含:
```json
{
"devicePodId": "device-pod-71-freq",
"profileHash": "sha256:...",
"cloudApiUrl": "..."
}
```
本地 hint/cache 不得包含以下字段,也不得参与授权或执行路由:
- `gatewaySessionId`
- `resourceId`
- `capabilityId`
- `hostWorkspaceRoot`
- `hostCli`
- Windows workspace 路径
- probe UID、串口端口、Keil 路径等硬件路由字段
正式 profile 必须由 `cloud-api` 从 DB 读取;`hwlab-device-pod` 不接受浏览器、code agent 或 CLI 上传的 profile 作为执行依据。若 `hwlab-device-pod` 需要 profile snapshot,应只接受 `cloud-api` 内部服务凭据转发的 snapshot,或通过内部服务凭据向 `cloud-api` 拉取。该凭据不得挂载进 code agent session Pod。
## 内部架构
正式 device-pod 由 profile registry、job lifecycle、freshness/blocker、bounded output、gateway/device-host adapter 和 lease integration 组成。第一阶段只有一个 `hwlab-device-pod` Deployment 管理多个 `devicePodId`profile authority 和 user grant 在 cloud-api/Postgres 中,device-pod 服务只接受 cloud-api 内部调用。
当前 v02 部署中的 `hwlab-device-pod` 微服务实现情况见 [spec-v02-hwlab-device-pod-service.md](spec-v02-hwlab-device-pod-service.md)。
## Profile Shape
`device_pods.profile_json` 至少表达以下 server-side 字段:
```json
{
"schemaVersion": 1,
"devicePodId": "device-pod-71-freq",
"target": {
"id": "target-id"
},
"projectWorkspace": {
"workspaceRoot": "F:\\Work\\Project",
"projectPath": "FirmWare/MDK-ARM/app.uvprojx",
"targetName": "app",
"hexPath": "FirmWare/MDK-ARM/app/app.hex"
},
"debugInterface": {
"type": "cmsis-dap",
"probeUid": "...",
"uv4Path": "C:\\Keil_v5\\UV4\\UV4.exe"
},
"ioInterface": {
"uart": [
{ "id": "uart/1", "port": "COM4", "baudRate": 921600 }
]
},
"route": {
"gatewaySessionId": "gws_...",
"resourceId": "res_...",
"capabilityId": "cap_...",
"hostWorkspaceRoot": "F:\\Work\\Project",
"hostCli": "node tools\\device-host-cli.mjs"
}
}
```
`profile_json` 不得保存 Git key、云 token、kubeconfig、数据库 URL 或长期 secret。`profile_hash``cloud-api` 对规范化 profile JSON 计算并在所有响应中返回;用户可见响应只能返回脱敏 profile 摘要和 hash。
## 数据表口径
正式规格推荐 `device_pods` 直接保存权威 profile 和 hash,避免额外 profile 微服务:
```sql
CREATE TABLE IF NOT EXISTS device_pods (
id TEXT PRIMARY KEY,
name TEXT NOT NULL DEFAULT '',
status TEXT NOT NULL DEFAULT 'active' CHECK (status IN ('active', 'disabled')),
profile_json TEXT NOT NULL DEFAULT '{}',
profile_hash TEXT NOT NULL DEFAULT '',
created_at TEXT NOT NULL,
updated_at TEXT NOT NULL
);
```
历史版本中的 `profile_ref``gateway_ref``device_pod_json` 可以在迁移时折叠进 `profile_json`。第一阶段不新增 `device_pod_profile_revisions`;需要审计版本、回滚或多环境批准时再引入 profile revision 表。
`device_pod_grants` 仍只表达用户是否拥有完整使用权;它不保存 profile,也不拆 capability。
## REST API
## API 接口说明
用户态 API 只经过 `cloud-api` 暴露:
```text
GET /v1/device-pods
GET /v1/device-pods/{devicePodId}/status
GET /v1/device-pods/{devicePodId}/debug-probe/chip-id
GET /v1/device-pods/{devicePodId}/io-probe/uart/1
GET /v1/device-pods/{devicePodId}/io-probe/uart/1/tail?maxBytes=12000
POST /v1/device-pods/{devicePodId}/jobs
GET /v1/device-pods/{devicePodId}/jobs/{jobId}
GET /v1/device-pods/{devicePodId}/jobs/{jobId}/output
POST /v1/device-pods/{devicePodId}/jobs/{jobId}/cancel
POST /v1/device-pods/{devicePodId}/leases
GET /v1/device-pods/{devicePodId}/leases/current
DELETE /v1/device-pods/{devicePodId}/leases/current
```
管理员 API 由 `cloud-api` 提供:
```text
POST /v1/admin/device-pods
PUT /v1/admin/device-pods/{devicePodId}
POST /v1/admin/device-pod-grants
DELETE /v1/admin/device-pod-grants/{devicePodId}/{userId}
```
`POST /v1/device-pods/{devicePodId}/jobs``intent` 表达具体业务,避免把 REST surface 扩张成大量一次性 route
```json
{
"intent": "workspace.build",
"args": { "profile": "debug" },
"reason": "DEV smoke"
}
```
第一阶段 intent 集合:
- `workspace.ls`
- `workspace.bootsharp`
- `workspace.cat`
- `workspace.rg`
- `workspace.apply-patch`
- `workspace.put`
- `workspace.rm`
- `workspace.rmdir`
- `workspace.keil`
- `workspace.build`
- `debug.status`
- `debug.chip-id`
- `debug.download`
- `debug.reset`
- `io.ports`
- `io.uart.read`
- `io.uart.read-after-launch-flash`
- `io.uart.write`
- `io.uart.jsonrpc`
`GET /debug-probe/chip-id``GET /io-probe/uart/1``GET /io-probe/uart/1/tail` 是用户态便捷 REST surface,但不能停留在静态面板或 fake probecloud-api 必须在完成 authenticate/grant 后创建对应只读 job,再经 `hwlab-device-pod` executor/gateway/device-host-cli 执行或返回同一套 blocker。
所有 job/status/output 和 probe GET 响应必须包含 `devicePodId``targetId``profileHash``traceId``operationId``status``freshness``blocker` 和 bounded output metadata。job output 文本默认最大 12000 bytes;超出时必须设置 `truncation.truncated=true``truncation.originalBytes`,并避免把完整 executor/gateway 原始输出嵌回 JSON。真实硬件响应不得把 fake、dry-run、SOURCE、LOCAL 或过期缓存标为 `DEV-LIVE`
## 微服务职责
| 服务 | 职责 |
| --- | --- |
| `hwlab-cloud-api` | 用户身份、admin/user、device grant、lease、profile authority、用户态 REST API、转发到内部 device-pod。 |
| `hwlab-device-pod` | 多 `devicePodId` 运行 registry、profile runtime validation、job store、freshness、bounded output、gateway/device-host-cli adapter。 |
| `device-pod-cli` | 把 `devicePodId:surface:path operation args` 1:1 转成 cloud-api REST;不保存权威 profile、不读取本地 profile 作为默认 authority、不直连 gateway。 |
| `device-host-cli` | Windows host 侧自包含业务工具,负责 Keil、pyOCD、UART、workspace 文件操作。 |
| `hwlab-gateway` | 只做受控 transport,不理解用户权限和 device-pod 授权。 |
| `hwlab-cloud-web` | 展示用户可见 device pod、admin 管理 profile/grant、显示 job/status/freshness。 |
## Kubernetes 口径
v0.2 第一阶段使用一个 `hwlab-device-pod` Deployment 和一个 ClusterIP Service
```text
hwlab-v02/hwlab-device-pod
replicas: 1
manages: many devicePodId
```
不为每个 `devicePodId` 创建 Deployment、Service、Ingress、Secret 或 namespace。这样更符合当前规模:运维对象少、GitOps diff 少、问题定位简单,也不会把设备数量直接放大成 k8s 资源数量。
只有在满足以下条件时,才考虑拆分为多个 `hwlab-device-pod` shard 或 per-device workload
- 单个服务内 job 队列和 freshness 监控互相影响;
- 不同设备需要不同 host network、USB、Secret 或资源 request
- 设备数量增长到单实例状态管理明显吃力;
- 强隔离需求超过应用层 grant 和内部服务凭据能覆盖的范围。
普通用户和 code agent session Pod 不应直接调用 `hwlab-device-pod` Service。正式路径是 `code agent -> cloud-api -> hwlab-device-pod`
## 验收标准
- `device-pod-cli` 在正式模式下不读取 `.device-pod/<devicePodId>.json` 作为权威 profile,只向 cloud-api 提交 `devicePodId`、intent 和 args。
- 普通用户无授权时不能看到或使用任何 device pod;授权后拥有对应 device pod 的完整使用权。
- code agent 不能通过修改本地文件改变 gateway session、resource、host workspace、probe UID 或 UART port。
- `hwlab-device-pod` 不接受无内部服务凭据的 profile snapshot 或 job 请求。
- `hwlab-device-pod` 一个实例可以列出并执行多个 `devicePodId` 的状态/job。
- cloud-api compatibility fallback 只能返回 blocked authority payload,不得合成 fake device pod 数据或作为正式 device-pod DEV-LIVE 证据。
- 强副作用 job 必须有 reason,并在物理互斥需要时获取 `device_leases`
- `POST /v1/device-pods/{devicePodId}/leases` 只对已授权 actor 创建或刷新互斥租约,响应只返回一次性 `leaseToken`;后续强副作用 job 必须通过 `leaseToken``x-hwlab-device-lease-token` 证明持有租约。
- 撤销 device pod grant 必须释放该用户对同一 `devicePodId` 的活动 lease。
## CLI 实现口径
`tools/device-pod-cli.ts` 是 v0.2 正式 CLI 实现;HWLAB code-agent runner 内的稳定短入口是 `hwpod``/app/skills/device-pod-cli/scripts/device-pod-cli.mjs` 只作为兼容启动器,在 runner PATH 缺少 `hwpod` 时临时 fallback,并应记录为 runner image/package 摩擦点。正式 CLI 的默认行为是:
- `profile list/show` 调用 cloud-api `/v1/device-pods``/status`,只显示服务端脱敏 profile 摘要和 `profileHash`
- `setup first-admin``admin device-pod upsert/grant` 只作为 cloud-api REST wrapper,用于首次空库 seed 或 admin profile/grant 管理;它们接受显式 `--profile-json``--device-pod-json`,不得读取本地 `.device-pod/*.json` 作为权威 profile。
- `devicePodId:workspace|debug-probe|io-probe...` selector 转换为 `POST /v1/device-pods/{devicePodId}/jobs` 或 job status/output/cancel REST,不直接调用 `/v1/rpc/hardware.invoke.shell`
- `bootsharp --pod-id <devicePodId>``<devicePodId>:workspace:/ bootsharp` 都转换为 `workspace.bootsharp` job,用于返回 workspace tree、AGENTS.md 摘要和当前路径提示;该入口是上下文恢复和 DS 派单的首个探测动作,不读取本地 profile。
- workspace 写操作覆盖 `apply-patch``put``rm``rmdir``build``keil add-source/remove-source` 等 Keil 工程维护动作。
- 源码局部编辑优先使用 `apply-patch``put` 只用于明确的整文件写入或新文件创建。
- UART 业务覆盖 `read``write``read-after-launch-flash``jsonrpc`JSON-RPC 请求必须由 device-host-cli 校验 response id,除非显式传入允许 id mismatch 的业务参数。
- apply-patch 类失败必须返回可定位的 patch hint,例如缺少 `*** End Patch`、hunk 上下文不匹配或 header 错误;调用方应先重新读取目标文件再重试小 hunk,不应默认绕到整文件覆盖。
- mutating job 继续由 cloud-api 侧强制 lease/reasonCLI 只转发 `reason``leaseToken`,不在本地绕过。
- `profile create` 这类本地 profile bootstrap 在正式默认路径中返回 `legacy_profile_create_removed`;管理员应使用 cloud-api admin API 管理服务端 profile/grant。
- DS/device-pod prompt 和 skill 示例必须优先使用 `hwpod`,避免把长路径 wrapper 复制成常态命令;host job 轮询时 job id 必须作为 `status/output/cancel` 的紧随位置参数传入,不能放到 flags 后面或靠 shell 管道解析 JSON。
## D601 F103 v0.2 Gateway SOP
D601 Windows F103 gateway 的稳定命名使用 `gws_D601_F103`。不要因为当前 `devicePodId` 或样例设备名是 `device-pod-71-freq` / `71-FREQ`,把 gateway session 改成旧的 `gws_d601_win_71_freq`profile route 和 Windows gateway 运行脚本必须使用同一组 F103 命名。
当前正式接入的 F103 v2 逻辑设备名为 `D601-F103-V2`。它的 Windows workspace 固定为 `F:\Work\D601-HWLAB`Keil 工程为 `projects/01_baseline/Projects/MDK-ARM/atk_f103.uvprojx`Keil target 为 `USART`Keil 可执行文件为 `C:\Keil_v5\UV4\UV4.exe`UART 为 `COM9`/`115200`。v0.2 服务端 profile 必须使用 `devicePodId=D601-F103-V2`,不得继续把该 workspace 暴露成 `device-pod-71-freq`
Windows 侧固定入口:
```text
Task Scheduler: HWLAB-DevicePodGateway-D601-F103
run script: C:\Users\liang\device-pod-gateway-rust\run-D601-F103.cmd
local status: http://127.0.0.1:7001/status
cloud API: http://74.48.78.17:19667
```
`run-D601-F103.cmd` 中的关键环境变量应保持为:
```cmd
set "HWLAB_GATEWAY_CLOUD_URL=http://74.48.78.17:19667"
set "HWLAB_ENVIRONMENT=v02"
set "HWLAB_GATEWAY_ID=gtw_D601_F103"
set "HWLAB_GATEWAY_SESSION_ID=gws_D601_F103"
set "HWLAB_GATEWAY_RESOURCE_ID=res_windows_host"
set "HWLAB_GATEWAY_BOX_ID=box_windows_host"
set "HWLAB_GATEWAY_CMD_CAPABILITY_ID=cap_windows_cmd_exec"
set "HWLAB_GATEWAY_CMD_EXEC_ENABLED=1"
set "HWLAB_GATEWAY_MAX_INFLIGHT=4"
set "HWLAB_GATEWAY_CMD_TIMEOUT_MS=120000"
set "HWLAB_GATEWAY_PORT=7001"
```
v0.2 `D601-F103-V2` 的服务端 profile route 必须和 gateway 注册保持一致:
```json
{
"gatewaySessionId": "gws_D601_F103",
"resourceId": "res_windows_host",
"capabilityId": "cap_windows_cmd_exec",
"hostWorkspaceRoot": "F:\\Work\\D601-HWLAB",
"hostCli": "node tools\\device-host-cli.mjs"
}
```
重启 gateway 时使用计划任务,不从临时 shell 直接启动长期进程:
```cmd
schtasks /End /TN HWLAB-DevicePodGateway-D601-F103
schtasks /Run /TN HWLAB-DevicePodGateway-D601-F103
powershell -NoProfile -Command "Invoke-RestMethod http://127.0.0.1:7001/status | ConvertTo-Json -Depth 12"
```
通过 UniDesk Windows route 操作时,工作目录应直接定位到 gateway 目录,例如 `D601:win/c/Users/liang/device-pod-gateway-rust`,再读取 `run-D601-F103.cmd` 或查询 `/status``/status` 中至少应看到 `gatewaySessionId=gws_D601_F103``cloudUrl=http://74.48.78.17:19667``session.status=connected``outbound.lastPollError=null`
G14 v0.2 验收在 `G14:/root/hwlab-v02` 执行,先确认 Cloud Web 同源 CLI 能看到 device pod 状态,再用正式 `device-pod-cli` 创建并轮询 job
```bash
bun tools/hwlab-cli/bin/hwlab-cli.ts client device-pods status device-pod-71-freq \
--base-url http://74.48.78.17:19666 \
--full
COOKIE=$(node -pe 'require("./.state/hwlab-cli/session.json").cookie')
bun tools/device-pod-cli.ts device-pod-71-freq:workspace:/ ls \
--api-base-url http://74.48.78.17:19667 \
--cookie "$COOKIE" \
--reason "v02 gws_D601_F103 smoke" \
--timeout-ms 120000 | tee /tmp/hwlab-device-pod-job.json
JOB=$(node -e 'const fs=require("fs"); const payload=JSON.parse(fs.readFileSync("/tmp/hwlab-device-pod-job.json", "utf8")); console.log(payload.body.job.id)')
bun tools/device-pod-cli.ts job status --pod-id device-pod-71-freq "$JOB" \
--api-base-url http://74.48.78.17:19667 \
--cookie "$COOKIE"
bun tools/device-pod-cli.ts job output --pod-id device-pod-71-freq "$JOB" \
--api-base-url http://74.48.78.17:19667 \
--cookie "$COOKIE"
```
`D601-F103-V2` 的验收命令使用同一条 v0.2 CLI 链路,只替换 `devicePodId` 并从 `bootsharp` 开始:
```bash
bun tools/hwlab-cli/bin/hwlab-cli.ts client device-pods status D601-F103-V2 \
--base-url http://74.48.78.17:19666 \
--full
COOKIE=$(node -pe 'require("./.state/hwlab-cli/session.json").cookie')
bun tools/device-pod-cli.ts bootsharp --pod-id D601-F103-V2 \
--api-base-url http://74.48.78.17:19667 \
--cookie "$COOKIE"
LEASE=$(bun tools/device-pod-cli.ts lease acquire --pod-id D601-F103-V2 \
--api-base-url http://74.48.78.17:19667 \
--cookie "$COOKIE" \
--reason "D601-F103-V2 build/download validation" | node -e 'let s="";process.stdin.on("data",d=>s+=d).on("end",()=>console.log(JSON.parse(s).body.leaseToken))')
bun tools/device-pod-cli.ts D601-F103-V2:workspace:/ build start \
--api-base-url http://74.48.78.17:19667 \
--cookie "$COOKIE" \
--lease-token "$LEASE" \
--reason "D601-F103-V2 Keil build validation" \
--timeout-ms 120000
bun tools/device-pod-cli.ts D601-F103-V2:debug-probe download start \
--api-base-url http://74.48.78.17:19667 \
--cookie "$COOKIE" \
--lease-token "$LEASE" \
--reason "D601-F103-V2 Keil download validation" \
--capture-uart uart/1 \
--capture-duration-ms 8000 \
--timeout-ms 120000
```
Code-agent/DS runner 内执行同类验收时,把 `bun tools/device-pod-cli.ts` 换成 `hwpod`;只有 `hwpod` 不在 PATH 时才临时 fallback 到 `/app/skills/device-pod-cli/scripts/device-pod-cli.mjs`。UART 串口若被 Windows 侧工具占用,应把 COM busy/access denied 记录为可选串口证据缺口,Keil build/download 是否通过以 job output 中的 build summary、`Programming Done``Verify OK``Application running` 为准。
DS 或其他 code-agent runner 由 `hwlab-cli client harness submit` 触发时,控制面应保持短连接:提交后用 `client harness result``client harness trace --limit <N>``client harness audit --require-bootsharp` 轮询,不把 UniDesk `ssh/tran` 连接长期挂在一次 `wait` 上。`client harness wait` 只用于短窗口观察,CLI 会把超长等待压到低于 UniDesk 透传硬超时的安全窗口,并在 JSON 中返回后续短轮询命令。
job 观察优先使用 cloud-api job 入口,避免从 shell 管道里解析整段 JSON
```bash
hwpod job status --pod-id D601-F103-V2 <jobId> --api-base-url <url> --cookie "$COOKIE"
hwpod job output --pod-id D601-F103-V2 <jobId> --api-base-url <url> --cookie "$COOKIE"
```
`build status/output/cancel <jobId>``download status/output/cancel <jobId>` 只作为 selector 便捷别名;当 `<jobId>``job_devicepod_*` 这类 cloud-api job 时,别名必须映射到 `/jobs/<jobId>``/jobs/<jobId>/output`,不得误启动新的 build/download job。Keil host 侧子 job 仍保留 `build status <hostJobId>``download status <hostJobId>` 语义,用于查看 Windows `device-host-cli` 创建的嵌套 job。
搜索下载、烧录或串口证据时,带空格、管道符或较长正则的 `workspace rg` 应使用显式参数,避免本地 shell、selector 和 Windows host 三层参数解析产生歧义:
```bash
hwpod D601-F103-V2:workspace:/ rg \
--pattern "Programming Done|Verify OK|Application running|Error" \
--path projects/01_baseline/captures \
--api-base-url <url> --cookie "$COOKIE"
```
验收通过条件:`client device-pods status` 返回 `status=ok``blocker=null`gateway sessions 中 `gws_D601_F103` 为 online`device-pod-cli` job 进入 `completed`,且 D601 Windows gateway `/status.lastDispatch.operationId` 与该 job 的 `operationId` 一致并有 `lastResultAt`。旧 `gws_d601_win_71_freq` session 只能作为历史 stale 线索,不能作为 v0.2 F103 gateway 的目标命名。
## 测试规格
## T1
阅读 docs/reference/spec-device-pod.md,然后用 cli 手动测试以下内容:普通用户无 grant 时访问 `/v1/device-pods` 不得看到任何 device pod;获得 grant 后能看到对应 devicePodId 和脱敏 profileHash,不能看到 gatewaySessionId、hostWorkspaceRoot 或 probe UID 等敏感路由字段。
## T2
阅读 docs/reference/spec-device-pod.md,然后用 cli 手动测试以下内容:尝试通过本地 `.device-pod/*.json` 修改 gateway route 或 workspace route,正式 cloud-api/device-pod 路径必须忽略该本地文件并继续使用服务端 profile authority。
## T3
阅读 docs/reference/spec-device-pod.md,然后用 cli 手动测试以下内容:提交一个强副作用 job,例如 download/reset,缺少 reason 或 lease 时必须被拒绝;获得 lease 后响应必须包含 devicePodId、profileHash、traceId、operationId、freshness、blocker 和 bounded output metadata。
## T4
阅读 docs/reference/spec-device-pod.md,然后用 cli 手动测试以下内容:对授权 Device Pod 运行 `workspace put``workspace rm``workspace rmdir``workspace keil add-source/remove-source``io-probe jsonrpc``--dry-run` 与一次真实小闭环。确认请求只经过 cloud-api job REST,输出 intent、reason、lease、traceId 和 bounded output,不读取本地 profile 或直连 gateway。
## 规格的实现情况
| 规格项 | 状态 | 说明 |
| --- | --- | --- |
| 逻辑 device-pod 模型 | 已实现为规格 | 四要素、profile shape 和 Kubernetes 口径已定义。 |
| profile server authority | 部分实现 | cloud-api 保存正式 DB profile 并向用户返回脱敏摘要;device-pod executor 不接受用户上传 profile。 |
| 用户 grant + lease | 部分实现 | cloud-api 已实现 admin grant、可见性过滤、lease acquire/current/release、强副作用 job lease 校验和撤销授权释放 lease。 |
| REST/job API | 部分实现 | cloud-api 已实现 list/status/events/probe/job/output/cancel 和 lease API,并可把已授权 job 转发给内部 `hwlab-device-pod` executorexecutor 已实现内部 job create/get/output/cancel lifecycle 和 gateway/device-host-cli dispatch adapter,无在线 gateway/device-host-cli 时返回 blocker。 |
| G14 device-host 功能吸收 | 部分实现 | v0.2 job intent 已覆盖 workspace put/rm/rmdir、Keil 工程维护和 UART JSON-RPC,保持 cloud-api grant/lease/profile authority。 |
| 禁止 fake 作为 DEV-LIVE | 已实现/持续约束 | 规格和服务 payload 要求显式标记 fake/source。 |