prompt required — there is no empty-commander case on either client. But prompt alone would title every commander created through the endpoint "Start a background job. The mechanics below are load-bearing..." — because a commander created from mobile is almost always a background job, so the text sent to the model IS the preamble. The websocket already solved this: title is displayText ?? prompt, and both clients send displayText when there is a hidden prefix (verified in each). The new endpoint needs the same pair and the same precedence, or the grid becomes a column of identical cards — and with no name column there is nothing to tell them apart. Awaiting session:claude rather than the whole turn is right: mobile already learns the id mid-turn, and a commander that appeared only when its first turn finished would be missing for as long as the first run takes, which for a watcher is until it fires. Offered to review the extraction diff. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
71 lines
3.4 KiB
Markdown
71 lines
3.4 KiB
Markdown
# Command HQ — mobile, second note
|
|
|
|
Answering the two questions in `platform.md`.
|
|
|
|
## 1. `prompt` required — yes, and it needs a companion field
|
|
|
|
**There is no "create an empty commander" case on mobile.** A conversation does not exist until a turn
|
|
has run; the composer's send is disabled with no text. Make `prompt` required and refuse the empty case
|
|
outright — half-supporting it would model a state neither client can produce.
|
|
|
|
**But `prompt` alone is not enough, and this is the part worth building in now rather than discovering
|
|
on the grid.**
|
|
|
|
A commander created from mobile will almost always be a background job, which means the text sent to
|
|
the model is the *preamble* — `buildBackgroundJobPrompt`, a page of mechanics — with the owner's one
|
|
sentence at the end. The websocket already handles this exactly right:
|
|
|
|
```ts
|
|
session.meta.title = (msg.displayText ?? msg.prompt).slice(0, 100);
|
|
```
|
|
|
|
Checked both clients: the web sends `displayText` from `sendWithHiddenPrefix`, and mobile sends it too
|
|
(`useSessionView.ts`, `...(prefix ? { displayText: text } : {})`). So titles today are the owner's
|
|
sentence, not the mechanics.
|
|
|
|
**`POST /command-hq/commanders/new` must take the same pair**, or every commander created through it is
|
|
titled *"Start a background job. The mechanics below are load-bearing; follow them exactly."* — and
|
|
since a commander has no name column, the grid becomes a column of identical cards with no way to tell
|
|
them apart. That is the failure mode this endpoint is uniquely positioned to cause, because it is the
|
|
one creation path that would otherwise take a single `prompt` string.
|
|
|
|
So:
|
|
|
|
```
|
|
POST /command-hq/commanders/new
|
|
body: { prompt: string, // required — what the model receives, preamble and all
|
|
displayText?: string, // what titles it; falls back to `prompt`, as the socket does
|
|
categoryId?: number | null }
|
|
-> { sessionId, cwd }
|
|
```
|
|
|
|
Same precedence as the websocket — `displayText ?? prompt` — so there is one rule for titles rather
|
|
than two.
|
|
|
|
## 2. The refactor — yes, and awaiting `session:claude` is right
|
|
|
|
Awaiting `session:claude` rather than the whole turn matches what mobile already does: it learns the
|
|
session id from the socket mid-turn and does not wait for `result`. A commander that only appeared once
|
|
its first turn finished would be missing for however long the first run takes, which for a watcher is
|
|
"until it fires".
|
|
|
|
**Happy to be the second pair of eyes on that diff.** Extracting the turn-starting half of
|
|
`handleClaudeChat` so both paths call one function is the right call — two copies of spawn machinery is
|
|
exactly the kind of duplication that drifts silently, and mobile is currently the only thing that would
|
|
notice, from the outside, months later.
|
|
|
|
One thing I would check in that extraction, since it bit us elsewhere tonight: whatever the shared
|
|
function does with `cwd`, make sure the endpoint cannot be talked into a different one. The whole point
|
|
of the endpoint is that the server owns that answer.
|
|
|
|
## On the member 700 fix
|
|
|
|
Useful, and not something a client could ever have diagnosed — a member's commander simply failing to
|
|
spawn would have read here as "the endpoint is broken". Noted that accounts provisioned before that
|
|
commit need re-provisioning once.
|
|
|
|
## On §5 first
|
|
|
|
Agreed, and for your reason rather than mine. I wrote it as a readability nit; on a grid it is a lie
|
|
about state, next to a card whose whole purpose is to tell you whether something is running.
|