Files
m3e-canvas/docs/handoff.md
T
pastilhasandClaude Fable 5 a0349cc7a3 the officer shell: designs per user, and the handoff desk
manifest, schema, router and two panels. the editor is hosted and
saving; the last step is not finished and the readme says so.

designs are rows scoped per user, with userId in every WHERE rather
than checked after the read — another person's design is 'not found',
so the id space cannot be probed. the Doc is stored whole as jsonb
because upstream adds fields to it constantly and none of them should
be a migration here.

the handoff is the reason the plugin exists and it is the part that
does not work: /api/chat has NO session-creation route, a session is
constructed by the browser opening a websocket, so a plugin has nothing
to POST to. onExport returns false and the clipboard still runs, which
is upstream's own behaviour — degraded, not broken. docs/handoff.md
names the one platform change that would finish it, and argues it
should be generic rather than ours.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-09-05 12:16:35 +01:00

75 lines
3.4 KiB
Markdown

# The handoff, and the one platform hook it needs
This plugin exists for a single step: **the generated prompt should not reach a clipboard.** It should
open a chat session with the agent, already containing the prompt, pointed at a directory on this
machine.
That step is not finished, and the reason is worth writing down precisely, because it is a platform
gap rather than an unwritten function.
---
## Why a plugin cannot do this today
**`/api/chat` has no session-creation route.** Reading `src/servers/api/chat/chat.ts`, the surface is
`GET /sessions`, `GET /sessions/:id`, `DELETE /sessions/:id`, the task verbs and the usage and model
readers. There is no `POST /sessions`.
A session is created by the **browser** opening `/api/chat/ws` and sending its first message. The
socket is the constructor. So there is nothing a plugin's server can POST to in order to start one,
and nothing it can hand a prompt to.
That is a reasonable design — it keeps session lifetime tied to a live connection — but it means
"start a session containing this text" is not expressible from a plugin.
## What is built
`POST /api/m3e-canvas/_handoff` takes `{ prompt, title }`, holds it in memory against a one-time id
with a ten-minute expiry, and answers:
```json
{ "id": "…", "chatUrl": "/chat/new?handoff=…", "ready": false }
```
`GET /api/m3e-canvas/_handoff/:id` collects it once and forgets it. A handoff belongs to whoever made
it; another user asking is told it does not exist rather than that it is theirs.
`ready: false` is the honest part of the response. **Nothing reads `?handoff=` yet.**
Meanwhile `onExport` returns `false`, so the editor still writes the clipboard. The feature is
degraded rather than absent: you get the prompt, you just paste it yourself — which is exactly what
upstream does, so nothing is worse than before.
## What would finish it
One small change, in the platform rather than here. `/chat/new` reads a `handoff` search parameter, and
when present fetches `/api/m3e-canvas/_handoff/:id` and puts the prompt in the composer before the
first send.
That is a few lines in `SessionListPage`, and it is deliberately **not** written here for two reasons.
It is a platform change, so it belongs to whoever owns the chat screen rather than arriving as a side
effect of installing a drawing tool. And a plugin reaching into another surface's composer is the kind
of coupling that is easy to add and unpleasant to find later.
### The shape worth arguing about first
If this is worth doing at all, it is probably worth doing **generically** — `?handoff=<pluginPrefix>/<id>`,
resolved against the plugin that minted it, so the next plugin that wants to hand work to the agent
does not add a second parameter. `todo/sendoff/` is already circling the same idea from another
direction.
Until that is decided, the seam here is small enough to throw away and specific enough to be honest
about what it does not do.
## The other half, unstarted
Even with the composer prefilled, "the agent writes the app" needs somewhere to write it. A design has
no notion of a project directory, and picking one is a product decision:
- a directory the user names when handing off
- a per-design directory remembered on the row
- whatever the chat session's own working directory already is
The third is the least work and probably the right default, since a session started from a dashboard
already has a directory. It is not implemented, and the row has no column for it.