# Architecture

## Status

This document records the selected backend boundaries for the AI agent expansion. The public Agent API is still intentionally undefined.

## Selected components

| Boundary | Selection | Responsibility |
| --- | --- | --- |
| Web | React, TypeScript, Vite, Monaco | Exercise editing, autosave state, Run controls, conversation UI, and authorized result streams. |
| Public API | Fastify on Node.js/TypeScript | Authentication, ownership, quotas, durable record creation, enqueueing job IDs, cancellation, and SSE. |
| Durable state | Supabase Auth, PostgreSQL, and Supabase Queues | Exercises, revisions, conversations, ordered turns, tool steps, run snapshots, checkpoints, and two queues: `agent_turns` and `code_runs`. |
| Agent harness | Vercel AI SDK Core in the worker | Provider-neutral messages, streaming, structured output, and typed tool calls. |
| Model gateway | OpenRouter candidate | Routes inference and permits model changes. The gateway and default model remain open decisions. |
| Worker | One TypeScript background service with two independent consumers | Claims agent turns and code runs with separate concurrency limits. |
| Code execution | Modal Sandboxes | Runs immutable Python or TypeScript snapshots with strict resource, network, time, and output limits. |

Selecting Vercel AI SDK Core selects an embedded open source library. It does not select Vercel hosting or Vercel AI Gateway. The model gateway is isolated behind an internal adapter so it can change without rewriting conversation persistence or tools.

## Runtime flow

1. The browser sends an authenticated edit, run request, or agent message to Fastify.
2. The API validates ownership and quotas, writes the durable record, and enqueues only its ID in the matching logged queue within the same database transaction.
3. The worker claims the job with a visibility timeout.
4. For an agent turn, the worker reconstructs context from PostgreSQL and invokes the model through Vercel AI SDK Core and the configured gateway.
5. Each model response, tool request, tool result, usage record, and terminal state is persisted as an idempotent checkpoint before the queue item is acknowledged.
6. The run-code tool creates a normal `code_runs` record and queue item. The run consumer starts a fresh Modal Sandbox and persists bounded output and status.
7. The API streams authorized persisted events to the browser. A retried worker resumes from the last completed checkpoint instead of repeating completed work.

## Durable harness responsibilities

The AI SDK is the in-process model and tool interface. The application owns durability:

- Serialize turns within each conversation.
- Claim jobs with visibility timeouts and bounded retries.
- Store model ID, token usage, gateway cost data, tool arguments, tool results, and related run IDs.
- Give every turn and tool step an idempotency key.
- Checkpoint before acknowledging queue work.
- Enforce maximum model steps, tokens, elapsed time, sandbox runs, and cost per turn.
- Resume incomplete turns from durable state after process restarts.
- Mark final success, failure, cancellation, or quota exhaustion explicitly.

## First tool boundary

The initial agent tool is `run_code`. It accepts a validated reference to an immutable exercise revision or recorded source snapshot and the allowed runtime inputs. It cannot choose shell commands, container images, mounts, credentials, or network policy. Manual runs and agent runs use the same `code_runs` path and quotas.

## Cost boundary

Vercel AI SDK Core adds no mandatory hosted-service or license charge. Worker compute, PostgreSQL/queue activity, model inference, gateway fees, and Modal usage remain distinct cost centers in the [cost model](cost-model.md).

## Open decisions

- Public Agent API endpoints and streaming event contract.
- Default and fallback models.
- Final model gateway: OpenRouter, Vercel AI Gateway, or direct providers.
- Conversation retention and context summarization.
- Queue visibility timeout, retry count, and dead-letter handling.
- Whether Run saves pending edits or captures an unsaved client snapshot.

## Public documentation deployment

Public documentation lives in tracked `docs/`, with a navigation homepage,
HTML product and architecture pages, and public Markdown planning sources.
Top-level `styles/` owns product tokens, controls, and workbench layouts shared
by the app and product documentation. Documentation presentation CSS stays in
`docs/css/`, and application document resets stay in `web/src/design/base.css`.

With Node.js 22, `node scripts/build-docs.mjs` copies `docs/` and `styles/` into
ignored `dist-docs/`. Cloudflare Pages should use that build command from the
repository root and publish `dist-docs/`. Git integration watches `docs/*`, `styles/*`, and
`scripts/build-docs.mjs`. Cloudflare Pages wildcards include path separators,
so nested documentation and style changes match.
Cloudflare Pages project `train-online-docs` is deployed publicly at
https://train-online-docs.pages.dev/ from `main` commit `ec73cb7`.
The initial deployment and public HTML/CSS/Markdown files were verified.
Automatic deployments and those watch paths are saved and verified in
Cloudflare. The push-triggered deployment test passed for commit `11fdcf3`
(deployment `83db04fc-dd80-4681-82cf-bb0aa0b45182`): temporary homepage text
appeared on the production URL. The test text is removed from the source.
Private working notes remain in ignored `dev-docs/`.
