Files
sift/docs/plans/p6/09-cli-docs.md
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

4.0 KiB

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

// 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.
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.