writeonce/docs/stories/jarvis/01-chat-loop.md
shoney.arickathil ad088163cc docs(rv2-tls,jarvis): TLS ladder through F3c-core + SAN landed
- rv2 9 phase-F row + review_pending: F3c-core sans-io driver + SAN/host
  landed (KAT'd vs RFC 8448 record trace); remaining F3c-net = system CA
  trust-anchor walk + net.connect_tls VM plumbing (live-gated), then G
- jarvis 00-story + 01 blocker tables: crypto/handshake engine landed;
  jarvis now waits only on net.connect_tls (the socket glue)
- status board rv2 9 row updated to the full ladder state

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
(cherry picked from commit 4fdf07196d01c32d0ab12d32d36fa4285ff34654)
2026-09-15 01:15:31 +02:00

5.1 KiB
Raw Blame History

track iteration status readiness review_pending
jarvis 1 pending ready 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. 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 TLS expose net.connect_tls. The crypto + handshake engine is landed and RFC-8448-gated (A–E, F1–F3c-core, the sans-io client driver, SAN/hostname); what remains is F3c-net — the socket glue that drives the driver over a real fd, plus a system CA trust-anchor walk. 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'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); 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 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.
  • Retrieval / embeddings — iteration 3.
  • 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.