From 97f31315bd1e25b8cd1423d8237b4406d472c36e Mon Sep 17 00:00:00 2001 From: SinachPat Date: Sat, 15 Aug 2026 20:07:44 +0100 Subject: [PATCH] docs: ADRs 0011-0013 for Sprint 1 stack, user store, and Docker boundary --- docs/decisions/0011-fastify-react-stack.md | 26 +++++++++++++++++++++ docs/decisions/0012-in-memory-user-store.md | 26 +++++++++++++++++++++ docs/decisions/0013-docker-boundary-mock.md | 26 +++++++++++++++++++++ docs/decisions/README.md | 3 +++ 4 files changed, 81 insertions(+) create mode 100644 docs/decisions/0011-fastify-react-stack.md create mode 100644 docs/decisions/0012-in-memory-user-store.md create mode 100644 docs/decisions/0013-docker-boundary-mock.md diff --git a/docs/decisions/0011-fastify-react-stack.md b/docs/decisions/0011-fastify-react-stack.md new file mode 100644 index 0000000..a19ebc7 --- /dev/null +++ b/docs/decisions/0011-fastify-react-stack.md @@ -0,0 +1,26 @@ +# 11. Fastify is the API server; React + Vite is the web shell + +- **Status:** Accepted +- **Date:** 2026-08-15 + +## Context + +IMPLEMENTATION §1 names "Express/Fastify" for the backend and "React + TypeScript" for the frontend. Sprint 1 required committing to one server framework before the first route and test. + +## Decision + +Use **Fastify** for `api/`, and **React 19 + Vite + vitest + @testing-library/react** for `web/`. + +### Options considered + +- Express (matches the plan's pseudocode). +- Fastify (chosen). + +### Rejected + +- Express — the plan allows either; Fastify ships built-in JSON-schema validation, native async handlers, and first-class TypeScript, which removes glue the plan would otherwise write by hand. + +## Consequences + +- Route handlers return via Fastify's `reply` object and validate payloads schema-first (the signup route enforces email format and password length). +- Future routes should keep using Fastify schema validation at the boundary rather than hand-rolled checks. diff --git a/docs/decisions/0012-in-memory-user-store.md b/docs/decisions/0012-in-memory-user-store.md new file mode 100644 index 0000000..cebc9d1 --- /dev/null +++ b/docs/decisions/0012-in-memory-user-store.md @@ -0,0 +1,26 @@ +# 12. In-memory user store behind a UserStore interface; Postgres deferred + +- **Status:** Accepted +- **Date:** 2026-08-15 + +## Context + +IMPLEMENTATION §1 names PostgreSQL for Wursor's own data. Sprint 1's first slice needed working sign-up/auth without standing up a database, migrations, or a connection pool on day one. + +## Decision + +Define a `UserStore` interface and ship `InMemoryUserStore` behind it. Passwords are hashed with `node:crypto` scrypt; session tokens are `crypto.randomBytes(32)` hex. + +### Options considered + +- Stand up Postgres now. +- In-memory store behind an interface (chosen). + +### Rejected + +- Postgres now — adds infrastructure friction to the first slice for no behavioral gain; the interface confines the swap to `services/user-store.ts`. + +## Consequences + +- Auth data is not durable until Postgres lands; restarting the API clears users and sessions. +- The Postgres swap is a drop-in replacement of `InMemoryUserStore` implementing the same `UserStore` contract. diff --git a/docs/decisions/0013-docker-boundary-mock.md b/docs/decisions/0013-docker-boundary-mock.md new file mode 100644 index 0000000..adfb821 --- /dev/null +++ b/docs/decisions/0013-docker-boundary-mock.md @@ -0,0 +1,26 @@ +# 13. Sandbox orchestration mocks the Docker boundary; real daemon client deferred + +- **Status:** Accepted +- **Date:** 2026-08-15 + +## Context + +The development machine has no Docker daemon (the Phase 0 spikes already hit this). Sprint 1 still had to build and test the sandbox services: DB subset, media proxy, manifest delta, GC, and orchestration. + +## Decision + +Implement the pure-logic sandbox services (`subset`, `media-proxy`, `manifest`, `gc`) and test them directly. Define a `DockerClient` interface and a `SandboxManager` orchestrator that depends on it, tested with a fake client. Defer the real Docker daemon HTTP client. + +### Options considered + +- Block on a Docker host. +- Mock at the boundary (chosen). + +### Rejected + +- Block on Docker — IMPLEMENTATION §7 already specifies "mocks at boundaries"; blocking would stall the slice for a reason that doesn't change the logic. + +## Consequences + +- `subset` and `media-proxy` logic is promoted from the golden harness (`e2e/golden/src/`) into `api/src/sandbox/`. +- The Docker wire-up (`DockerClient` daemon implementation, `image-manager`, `warm-pool`) is an explicit Sprint 1 follow-up and remains unverified until a Docker host exists. diff --git a/docs/decisions/README.md b/docs/decisions/README.md index 6893985..8d705d8 100644 --- a/docs/decisions/README.md +++ b/docs/decisions/README.md @@ -24,6 +24,9 @@ Each ADR is a single file following the [Nygard format](https://cognitect.com/bl | [0008](0008-empty-packages-not-stubs.md) | Workspace ships empty packages, not placeholder source | Accepted | | [0009](0009-repo-rename.md) | Repository renamed originmain → wursor | Accepted | | [0010](0010-openrouter-live-golden.md) | Golden harness scores live runs through a provider-agnostic LLM client (OpenRouter first) | Accepted | +| [0011](0011-fastify-react-stack.md) | Fastify is the API server; React + Vite is the web shell | Accepted | +| [0012](0012-in-memory-user-store.md) | In-memory user store behind a UserStore interface; Postgres deferred | Accepted | +| [0013](0013-docker-boundary-mock.md) | Sandbox orchestration mocks the Docker boundary; real daemon client deferred | Accepted | ## How to add one