---
name: alchemy-shell-payload-mechanics
description: "How to actually drive the alchemy Requests shell: refs need the # sigil, and `shell send --file` STAGES a file for the next create rather than posting it — a comment must go through the text form."
metadata: 
  node_type: memory
  type: reference
  originSessionId: 7537c945-a83b-4fc2-bfd9-184eb364b28e
  modified: 2026-09-09T03:29:58.371Z
---

Driving alchemy through `spt shell` has two shapes that both fail in quiet ways. Measured
2026-08-21 while updating Requests #11/#12 on `claude-spt-bs`.

1. **Refs need the `#`.** `spt shell cmd <ref> state 12 done` is rejected with
   `error: bad ref '12': expected #N, repo#N, or owner/repo#N`. Quote it so the shell does not eat
   it as a comment: `spt shell cmd <ref> state "#12" done`.
2. **`shell send <ref> --file <path>` does NOT post the file.** It STAGES it, and the reply says so:
   *"your next create reads its text as the request body; add `--attach` on line 1 to link it as an
   attachment instead."* So a `comment #12` first line inside that file is never read as a verb —
   the comment silently does not happen and a stray staged file waits to be absorbed by whatever
   `create` comes next.
3. **Post a comment through the TEXT form instead**, which does honor line 1 as the verb:
   `body=$(sed '1s/.*/comment #12/' file.txt); spt shell send <ref> "$body"`. Multi-line is fine as a
   single quoted argument. Confirmation comes back asynchronously as
   `comment <id> added to <owner>/<repo>#N`.

**Why:** every one of these returns `SHELL_SPOOLED:…`, which reads like success — the failure only
surfaces later, in an async reply, on a different turn. Same family as
[[msys-grep-instrument-traps]]: the reassuring answer arrives first.

**How to apply:** treat `SHELL_SPOOLED` as "accepted for transport", never as "the op worked", and
read the async reply before reporting a board update as done. `SHELL_WAKE:` in the response means
the instance was offline and was woken — expect the real answer one or two turns later, and do not
resend meanwhile ([[commune-path-in-wake]] has the same don't-poll discipline). Payload shape for
the text form: line 1 = verb + flags, line 2 = title, rest = body.

**THE 1600 CAP IS REAL AND IT IS `create`-ONLY.** A `create` body over it is REFUSED, async and by
name: *"error: body is 1750 characters — a create takes at most 1600 (comments have no limit). Mint
the Request with the ask itself, then `comment #N` with the detail: the discussion belongs on the
Request, not inside it."* So: mint short, then comment the analysis.

**Comments are UNCAPPED** (2026-09-09): a 1655-char comment posted intact, verified at the consumer,
and an earlier one on the same Request is 2060. Do not pre-split a comment or warn about truncation.
⭐⭐ I learned that half by over-correcting: I removed the create cap from this file while fixing the
comment half, and was refused by the very rule I had just deleted — **a replacement can delete a
property the old rule supplied incidentally, so enumerate what it guaranteed before rewriting it**
(the [[v0383-echo-commune-authorship]] lesson, paid again in a memory edit). ⭐ Verify a length limit by reading the posted body back
(`gh api repos/<o>/<r>/issues/comments/<id> --jq .body`, note the route is
`/issues/comments/<id>`, NOT `/issues/<n>/comments/<id>`, which 404s), not by trusting a remembered
number.

**A transport failure looks nothing like a cap.** The first attempt at that comment came back async
as `error: GitHub request failed: … tls connection init failed … (os error 10060)` — a network
timeout, while `git push` to the same host had succeeded minutes earlier. It was transient and the
plain retry posted. ⭐ Read the error before theorising: I had a truncation hypothesis ready and it
was the wrong failure entirely. Confirm exactly ONE comment landed after a retry — a failed attempt
that actually posted would leave a duplicate.

WRITE VERBS ARE LINE-1 VERBS (2026-08-21): a comment posts as a text send whose FIRST LINE is `comment #N`, body from line 2 on — `spt shell cmd <ref> comment` is not in the op vocabulary at all, and a polite lead-in line ("Please post this as a comment on #1.") is rejected: `error: please is a single-line verb — unexpected extra lines`. The rejection arrives ASYNC, a turn later, AFTER a reassuring `SHELL_SPOOLED` — so a write is not landed until its ack names the qualified ref (`comment <id> added to owner/repo#N`). `spt shell cmd <ref> help` prints the whole vocabulary.
