- rv2 9 §F3c-net marked LANDED + live-gated; phase-F row COMPLETE (client); frontmatter review_pending updated (client complete, remaining = G server + deferred park-handshake/TlsConn/pooling + doctrine-doc corrections) - jarvis 00-story + 01: the outbound-TLS blocker is cleared (net.connect_tls landed) — jarvis 1 (chat loop) is now buildable - status board: rv2 9 row + NEXT PLAN rewritten to the completed client; next step is jarvis 1 or rv2 9 G Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> (cherry picked from commit 732c2216b501dd226d306f9abcbd1ddd846809dc)
5.1 KiB
| 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_pendingfor the developer's second review — the defaults below are reasonable but were not individually confirmed.
The outbound seam has landed
The blockers are cleared: net.connect (✅) and runtime-v2
9's net.connect_tls / net.read_tls /
net.write_tls (✅ landed 2026-09-09, live-gated just tls) expose outbound
HTTPS from .wo, with the full hand-rolled TLS 1.3 handshake + chain/hostname
validation. Everything below is now buildable .wo on top of that seam plus
porch 2/3/6/7. (Note: the TLS builtins return the connection as an Int fd, per
the F3c-net object-model decision — the chat loop dials with them directly.)
Decisions locked (auto-approved, review pending)
- 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.
- Auth: an API key from the environment/config, sent as the
x-api-keyheader — never in a URL, never logged. Missing key is a startup refusal, not a runtime surprise. - The relay is an actor per conversation. A conversation actor owns the
upstream
net.connect_tlsconnection, parses the LLM's SSE (content_block_deltatext 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). - Durable history in two
@tableclasses:Conversation { id @unique, principal, created_at }andMessage { 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. - 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. - 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
@tableclasses, 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.
justa 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.