fix(device-pod-cli): add selector cheat sheet and fail-fast hints

Address the friction in #760 where Code Agent picked <pod>:workspace:/ keil
download/flash and <pod>:debug-probe flash for firmware flashing, all of
which are not SOP. Add to SKILL.md:

- Selector Cheat Sheet: explicit per-surface allowed operations table
- Common selector mistakes table: wrong selector -> error you would hit
  -> correct selector
- Pitfalls Observed in Real Runs: --reason required for build/download
  even on read-only sub-actions, cloud-api job output is empty for
  build/download, workspace rg --pattern shell-pipe collision

In tools/src/device-pod-cli-lib.ts:
- workspace.keil now fail-fasts on any action that is not add-source or
  remove-source, with code unsupported_workspace_keil_action and a
  selector hint pointing to debug-probe download start
- debug-probe flash now has a dedicated error code path with the same
  selector hint instead of the generic unsupported_debug_operation
- catch-all debug-probe error message now includes the allowed list
- help() payload exposes a selectorCheatSheet array so hwpod --help
  callers see the SOP table without reading the SKILL.md file

Tests in tools/device-pod-cli.test.ts: 6 new cases lock in the new
error codes, the selector cheat sheet, and that add-source still
dispatches correctly. 26/26 pass.
This commit is contained in:
Codex Device-Pod Agent
2026-06-03 16:59:17 +08:00
committed by Codex
parent 0d06a4c5fe
commit 5c35f8349e
3 changed files with 202 additions and 2 deletions
+94
View File
@@ -37,6 +37,100 @@ HWLAB_DEVICE_POD_SESSION_TOKEN=<assembled-device-pod-session-token>
`hwpod` automatically locates cloud-api from the assembled runtime env and automatically uses `HWLAB_DEVICE_POD_SESSION_TOKEN`. When `HWLAB_RUNTIME_ENDPOINT_LOCKED=1`, passing `--api-base-url`, `--api-url`, `--cloud-api-url`, or `--base-url` is a hard error. This prevents stale trace examples or model guesses from overriding the current cluster/lane.
`--api-base-url` and `--session-token` are local-debug-only options outside locked HWLAB runners. Do not use them in normal DS / AgentRun / Cloud Workbench flows.
## Selector Cheat Sheet
The selector is `<devicePodId>:<surface>:<path> <operation> [args...]`. The CLI
dispatches to a fixed set of operations per surface. Anything outside this
table is rejected either fail-fast in the TS CLI (`unsupported_*` code) or by
the host tool after dispatch. Always pick from this table; if a need is not
listed, that is a missing intent - file an issue instead of guessing.
| Surface | Selector path | Allowed operations (intent) | Notes |
| --- | --- | --- | --- |
| `workspace` | `/` or `<relpath>` | `ls`, `cat`, `rg`, `bootsharp`, `apply-patch`, `put`, `rm`, `rmdir`, `keil add-source`, `keil remove-source`, `build start\|status\|output\|cancel\|wait` | `keil` is for **project-file maintenance only**. Compile/link goes through `build start`, not through `keil`. |
| `debug-probe` | `:<probe>` (default `/`) | `status`, `chip-id`, `download start\|status\|output\|cancel\|wait`, `reset` | Firmware flash is `download`, not `flash`. There is no `debug-probe flash` op. |
| `io-probe` | `:/uart/<n>` | `read`, `write`, `jsonrpc`, `read-after-launch-flash`, `ports` | UART id is a strict path token (`/uart/1`); do not split it across argv. |
### Common selector mistakes (do not retry - pick the right one)
| Wrong selector | Error you would hit | Correct selector |
| --- | --- | --- |
| `<pod>:workspace:/ keil download` | `unsupported workspace keil command: download` (host-side, after dispatch) | `<pod>:debug-probe download start --reason "..."` |
| `<pod>:workspace:/ keil flash` | `unsupported workspace keil command: flash` | `<pod>:debug-probe download start --reason "..."` |
| `<pod>:debug-probe flash` | `unsupported_debug_operation: flash` (TS-side, fail-fast) | `<pod>:debug-probe download start --reason "..."` |
| `<pod>:workspace:/ build` | `workspace.build` job runs but with empty `action`; always pass `start` | `<pod>:workspace:/ build start --reason "..."` |
If a CLI call returns an `unsupported_*` error, do not retry the same
selector - re-check the table and switch to the right one. When the right
selector still does not exist, file an issue against `pikasTech/HWLAB` with
the selector name and the underlying intent you wanted.
## Pitfalls Observed in Real Runs
These are concrete friction points seen in production runs against
`D601-71-FREQ` and `D601-F103-V2`. They are not bugs in the SOP but are
worth knowing before you start a long build+download job.
### 1. `--reason` is required for mutating `workspace.build` / `debug-probe.download`
`cloud-api` enforces `device_job_reason_required` on any job whose intent
is `workspace.build` or `debug-probe.download`, even when the sub-action
is `status` / `wait` / `output` / `cancel` and even when the job id points
to an already-completed host sub-job. Always pass `--reason "<short text>"`
for these selectors, including read-only `build status` and
`download output`:
```sh
# ok
hwpod <pod>:workspace:/ build status <jobId> --reason "poll previous build"
hwpod <pod>:debug-probe download output <jobId> --reason "poll previous download"
# fails with device_job_reason_required
hwpod <pod>:workspace:/ build status <jobId>
```
If you do not have a meaningful business reason yet, use a placeholder
like `--reason "v0.2 smoke"` and surface that in the trace.
### 2. `job output` for build/download is empty; host evidence is on the Windows host
For `intent=workspace.build` and `intent=debug.download` jobs, cloud-api's
`body.output.text` is currently `""` and `body.bytes == 0`. The
`Programming Done` / `Verify OK` / `Application running` / `0 Error(s)` /
`0 Warning(s)` evidence is on the D601 host in
`F:\\Work\\ConStart\\.device-pod\\.state\\device-host-cli\\keil\\`.
Do not loop on `workspace rg` looking for those strings - that selector
sees the project source tree, not the host job log directory.
Until cloud-api propagates host `logTail` / `buildSummary` to
`body.output.text` (tracked separately), the only SOP-compatible
verification path is:
1. `hwpod job status --pod-id <pod> <jobId>` returns `status=completed`
2. `hwpod <pod>:workspace:/ bootsharp` re-runs and shows the workspace is
unchanged (host job store keeps the latest log file)
3. SSH into D601 directly (out of band) and read the matching
`build_FREQ_Controller_FW_<ts>.log` / `download_FREQ_Controller_FW_<ts>.log`
under `.device-pod\\.state\\device-host-cli\\keil\\`
If you need the evidence in-band, file a follow-up issue against
`pikasTech/HWLAB` requesting a `download evidence` selector that returns
host `logTail` + `buildSummary` directly through the formal REST job path.
### 3. `workspace rg --pattern "regex|pipes"` collides with local shell
When the regex contains `|` or other shell metacharacters, the local shell
interprets them before `hwpod` sees the pattern. Two safe forms:
```sh
# shell-safe: pattern as one arg, but | still needs quoting
hwpod <pod>:workspace:/ rg --pattern "Programming Done|Verify OK" --path <relpath>
# bullet-proof: pattern as base64
P=$(printf "%s" "Error\(s\)|Warning\(s\)|Erase Done|Programming Done|Verify OK|Application running" | base64 -w0)
hwpod <pod>:workspace:/ rg --pattern-b64 "$P" --path <relpath> --reason "log scan"
```
## Primary Commands