writeonce/docs/stories/jarvis/01-chat-loop.md
shoney.arickathil 9d1689be55 docs(jarvis): create iteration stories 1 (ready) + 2/3 (refine)
- jarvis 1 (chat loop) brainstormed to ready with forks AUTO-APPROVED for
  autonomous execution and flagged in `review_pending` frontmatter for the
  developer's second review: Anthropic Messages API backend, env-var API key,
  actor-per-conversation SSE relay, durable @table history, session-gated routes
- jarvis 2 (tool use) + 3 (retrieval/RAG) created at refine with forks named
- 00-story iterations table linked to the new files
- blocked until rv2 9 TLS reaches phase F; pure .wo on porch 2/3/6/7 + the seam

(cherry picked from commit 8e160c3fcf9c50a05af073c036c693b192c9e595)
2026-09-15 01:15:31 +02:00

96 lines
4.9 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

---
track: jarvis
iteration: "1"
status: pending
readiness: ready
review_pending: "forks auto-approved 2026-09-08 for autonomous execution — developer second review before code lands"
---
# jarvis 1 — the chat loop: a prompt in, streamed tokens out, durable history
> Part of [Story — jarvis, the writeonce AI assistant](00-story.md).
> The whole end-to-end seam: a browser sends a prompt, jarvis relays it to an
> LLM over HTTPS, streams the reply back token by token, and persists the
> conversation durably. Nothing past this rung is worth building until it runs.
>
> **Forks auto-approved (2026-09-08) for autonomous execution and flagged in
> `review_pending` for the developer's second review** — the defaults below are
> reasonable but were not individually confirmed.
## Blocked until the outbound seam lands
This iteration cannot run until `net.connect` (✅ landed) and runtime-v2
[9](../runtime-v2/09-in-process-tls.md) TLS reach **phase F** (`net.connect_tls`
+ the handshake). Crypto A–D are landed; E (X.509) and F (handshake) remain.
Everything below is buildable `.wo` on top of that seam plus porch 2/3/6/7.
## Decisions locked (auto-approved, review pending)
1. **Backend: the Anthropic Messages API over HTTPS**, streaming (SSE). One
backend for v1; a thin adapter boundary so an OpenAI-compatible backend can
slot later without touching the chat loop. Endpoint, model id and version
header come from config.
2. **Auth: an API key from the environment/config**, sent as the `x-api-key`
header — never in a URL, never logged. Missing key is a startup refusal, not
a runtime surprise.
3. **The relay is an actor per conversation.** A conversation actor owns the
upstream `net.connect_tls` connection, parses the LLM's SSE
(`content_block_delta` text deltas), and forwards each delta to the browser
through porch [7](../porch/07-sse-and-compression.md)'s SSE. Backpressure and
disconnect cleanup follow porch 7's contract (a write to a gone client frees
the fiber, the upstream fd and the actor).
4. **Durable history in two `@table` classes**: `Conversation { id @unique,
principal, created_at }` and `Message { conv_id (indexed), seq, role,
content, created_at }`. Keyed to the session principal (porch
[3](../porch/03-sessions.md)); history replays after a restart with no
external store — the writeonce differentiator.
5. **Route surface, session-gated**: `GET /` (chat UI via wo-html/writeonce-view),
`POST /message` (accept a prompt, append it, start the stream), `GET /stream`
(SSE of the reply). The POST is CSRF-protected once porch
[4](../porch/04-csrf.md) is built; until then it is bearer/session-gated and
the gap is stated.
6. **One turn at a time, no tools.** No function-calling, no retrieval — those
are iterations 2 and 3. The reply is a single streamed assistant message.
## Phases
- **A — the backend client.** Over `net.connect_tls`, issue the Messages
request and parse the streamed SSE deltas into text. Adapter boundary isolated
so the wire format is one file.
- **B — the conversation store.** The two `@table` classes, append-message,
load-history, list-conversations; durable-replay proven.
- **C — the relay + web surface.** The conversation actor, the porch routes, the
wo-html chat page, browser SSE of the reply; session-gated.
- **D — the gate and the ledger.** `just` a jarvis sample end to end against a
**local stub** LLM server (no network in the gate): prompt → streamed reply →
durable history → restart-replay. Both the happy path and a mid-stream
disconnect. Record what landed.
## Acceptance Criteria
- **Given** a signed-in user and a prompt, **when** it is sent, **then** the
reply streams back token by token and both prompt and reply are persisted.
- **Given** a restart, **when** the user returns, **then** their conversation
history replays from the `@table`, no external store.
- **Given** the client disconnecting mid-stream, **when** the next delta
arrives, **then** the upstream fd, the fiber and the actor are freed.
- **Given** a missing API key, **when** the app starts, **then** it refuses
loudly rather than failing at first request.
- **Given** the gate, **when** it runs, **then** it exercises the whole path
against a local stub with no live network call.
## Out Of Scope
- **Tool use / function calling** — iteration [2](00-story.md).
- **Retrieval / embeddings** — iteration [3](00-story.md).
- **Multiple backends at once / model routing** — sketched in the overview,
later.
- **The live network in the gate** — the gate uses a local stub server; a real
API smoke test is a manual, keyed, out-of-gate step.
## Info
Pure `.wo` on porch 2/3/6/7 + the TLS seam; no new runtime work of its own. The
one hard dependency is runtime-v2 9 reaching phase F. The auto-approved forks
(backend choice, auth transport, store schema, route surface) are the developer
second-review items flagged in `review_pending`.