Files
sift/docs/plans/p6/09-cli-docs.md
T
kyleandClaude Opus 5.5 103ec3d05a P6 during the run: split the runner task (02 core, 03 workers); guard.PreCheck as an owner commit; AGENTS.md: write code into files early
Task 02 (the whole runner) ended at 21:08 with no file and no commit: Ornith wrote the
implementation out in its reply (~700 lines), OpenCode compacted the session, and the model
then emitted a malformed tool call. It had also looked for the given test at a wrong path
("_files/…" relative to the plan: my ambiguity).

- 02-runner → 02-runner-core (types, Submit, Status, Touch, Cancel; no goroutines) and
  03-runner-workers; the given test split to match (runner_core_test.go gains three tests:
  queued storage, cancel while queued, Status never moves the lease); later tasks renumbered
  04-09 and every cross-reference checked.
- Copy paths spelled from the repo root.
- guard.PreCheck (+ its test) is committed here by the owner: the old task 05 told the
  implementer to edit internal/guard/, which AGENTS.md forbids. My contradiction.
- AGENTS.md rule 6: write code into files early, in small steps.
Both runner test files compile against a panic-only skeleton (the core file also alone) and
fail there on panic: todo.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-26 21:14:08 -07:00

90 lines
4.0 KiB
Markdown

# P6 task 09: `sift batch`, README and skill
**Branch:** `p6` (`git switch p6`; `git status --short` must be empty, otherwise stop)
**Commit subject:** `cli: sift batch submit|status|get|cancel; README and skill`
## Goal
Agents reach the batch endpoint through the CLI, as they do `fetch` and `search`. There is no
Hermes plugin.
## Files
- Copy: `docs/plans/p6/_files/cmd_sift/batch_test.go` → `cmd/sift/batch_test.go`
- Create: `cmd/sift/batchcmd.go` (`batchMain`, `parseBatchArgs`, one small function per verb)
- Modify: `cmd/sift/main.go` (one `case "batch":` line and the usage text), `README.md`,
`skill/SKILL.md`, `docs/implementer-log.md`
## What already exists
- `cmd/sift`: `loadClient() (base, key string, err error)` reads `client.toml`; `searchcmd.go` is the
pattern for a verb (build the request with the bearer header, decode the JSON envelope, exit
non-zero on a non-2xx). Never put a key on the command line.
- The endpoints (task 07):
- `POST /v1/batch` `{"targets":[…]}` → `202 {"success","job_id","status","targets","reused"}`;
- `GET /v1/batch/{id}` (summary; `?results=1` inlines results);
- `GET /v1/batch/{id}/{idx}` (one result);
- `DELETE /v1/batch/{id}`.
## Interfaces added here
```go
// parseBatchArgs validates a `sift batch` command line (without "batch").
func parseBatchArgs(args []string) (verb string, rest []string, err error)
```
```
sift batch submit <targets.json|-> # prints {"job_id", "status", "reused"}
sift batch status [--results] <job_id> # prints the status body (a summary unless --results)
sift batch get <job_id> <idx> # prints one target's result, as `sift fetch --json` would
sift batch cancel <job_id> # prints {"success", "status"}
```
## Rules
1. `parseBatchArgs` accepts exactly the forms above:
- `submit` with one argument (`-` means stdin);
- `status` with a job id, optionally preceded by `--results`;
- `get` with a job id and an `idx` that is a non-negative integer;
- `cancel` with a job id.
Anything else (no verb, a missing or extra argument, an unknown verb, a bad `idx`) is an
error. `rest` is the arguments after the verb, unchanged.
2. `submit` reads the file and checks it locally before calling the server: it must be a JSON
object with a non-empty `targets` array. It may also be a bare array of targets or of URL
strings; wrap either into `{"targets": […]}`, turning each string into `{"url": s}`.
3. Every verb prints the server's JSON on stdout, and on a non-2xx prints the error envelope to
stderr and exits with `exitFor(status, msg)`, as `fetch` does.
4. **`README.md`**, a short "Batch" section:
- the four endpoints;
- the model: serial per job, one worker by default, round-robin across jobs;
- the caps (32 targets, 1 live job per caller, 8 in all) and the 256 KiB body;
- idempotency: the same list from the same caller returns the same job while it is live or
has results;
- the lease/TTL and restart semantics;
- the summary-vs-results reads;
- restricted-caller redaction;
- the `sift_batch_*` metrics.
5. **`skill/SKILL.md`**, a short section:
- for several URLs you will read later, `sift batch submit` them once, rather than a loop of
`sift fetch`;
- poll with `sift batch status`, and read each result with `sift batch get <job> <idx>`;
- don't use `--results` unless you need every result at once (it can be large);
- a batch is slow on purpose: for one URL you need now, use `sift fetch`.
## Steps
- [ ] **1.** `git switch p6`; copy the given test. **2. See it fail.**
- [ ] **3.** `batchcmd.go`. **4.** `main.go`. **5.** README and skill.
- [ ] **6.** `gofmt -w`; `go test -race -count=3 ./cmd/sift/` → `ok`. **7.** `make gate`.
- [ ] **8.** Row `p6/09-cli-docs`; commit.
```sh
git add cmd/sift README.md skill/SKILL.md docs/implementer-log.md
git commit -F .state/commit-msg.txt
```
## Done when
- The given test and every existing cmd test pass under `-race -count=3`; `make gate` ok; the
given file is byte-identical.