docs: record decisions ADR 0018-0021 (Next.js, sign-in methods, WeOwn Cloud, no-diff rule) and align PRD, guide and AGENTS.md
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014SdXCbrN5PtGExkpvMaMHx
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
# 11. Fastify is the API server; React + Vite is the web shell
|
||||
|
||||
- **Status:** Accepted
|
||||
- **Status:** Partially superseded by [ADR 0018](0018-nextjs-web-app.md) (the React + Vite web shell). Fastify for `api/` stands.
|
||||
- **Date:** 2026-08-15
|
||||
|
||||
## Context
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
# 18. The web app is Next.js (App Router); Fastify stays as the Wursor API
|
||||
|
||||
- **Status:** Accepted
|
||||
- **Date:** 2026-10-12
|
||||
- **Supersedes:** the web half of [ADR 0011](0011-fastify-react-stack.md). The Fastify half stands.
|
||||
|
||||
## Context
|
||||
|
||||
PRD Headless revision 4 (HL-01, HL-04, HL-07, HL-08, HL-15) requires a web app with a server layer that holds access and refresh tokens, so the browser only ever sees an HttpOnly session cookie and, while watching a run, a two-minute stream token. It also requires dynamically rendered authenticated pages that are never shared-cached, CSRF protection on state-changing requests, static marketing pages, and an installable PWA. [ADR 0011](0011-fastify-react-stack.md) chose React + Vite, which is a client-only shell with no server layer. `web/` currently holds the Sprint 1 and Sprint 2 components and hooks (signup, chat, preview, approve, site connector).
|
||||
|
||||
## Decision
|
||||
|
||||
Build the web app on **Next.js (App Router, TypeScript)**. Keep **Fastify** for the Wursor API in `api/`. Keep vitest and Testing Library for unit and component tests, and Playwright for end to end.
|
||||
|
||||
The Next.js server is the backend for frontend: it owns the session, holds tokens, validates input against the OpenAPI contract and calls the Wursor API on the person's behalf. It makes no authorization decisions; the Wursor API rechecks every permission. Run streams go directly from the browser to the Conductor, never through Next.js.
|
||||
|
||||
Existing `web/` React components and hooks are kept and moved under the Next.js app. The Vite shell is retired once the Next.js app serves the same screens.
|
||||
|
||||
### Options considered
|
||||
|
||||
- **Next.js (chosen).** Matches the PRD as written. Server Components, server actions and route handlers give the token-holding layer, per-request rendering and static marketing pages in one framework, and the team already writes React and TypeScript.
|
||||
- **Keep Vite and add a backend for frontend inside the Fastify API.** Smallest change to the repo, and one fewer framework. It puts session and cookie handling in the API, which the PRD wants kept separate from the control plane, and leaves marketing pages, SEO and PWA scope to be rebuilt by hand.
|
||||
- **Vite plus a separate small BFF service.** Clean separation, but one more deployable for a small team, which the PRD's risk register already flags.
|
||||
|
||||
### Rejected
|
||||
|
||||
- Keeping Vite alone: it cannot satisfy HL-04 (no tokens in the browser).
|
||||
- A separate BFF service: extra deployable with no benefit over the Next.js server layer.
|
||||
|
||||
## Consequences
|
||||
|
||||
- `web/` becomes a Next.js app. The migration is step C and H in [IMPLEMENTATION.md](../../IMPLEMENTATION.md) section 0.4; existing Sprint 1 and 2 screens move first, and the in-memory session token flow from Sprint 1 is replaced by server-held sessions.
|
||||
- `web/package.json` changes its dev, build and start scripts. Tests stay on vitest.
|
||||
- The 90% coverage floor for `web/` continues to apply. Server Components and server actions are tested at the unit level where possible and covered by Playwright otherwise.
|
||||
- Hosting for the Next.js app must support server rendering and streaming responses (PRD section 21).
|
||||
- No more Vite-specific code is added to `web/` from this point.
|
||||
@@ -0,0 +1,37 @@
|
||||
# 19. Sign-in supports an emailed code, an emailed link and passkeys
|
||||
|
||||
- **Status:** Accepted
|
||||
- **Date:** 2026-10-12
|
||||
|
||||
## Context
|
||||
|
||||
The PRD text (HL-03) says people sign in with a passkey or an emailed link. The finished design (PX-01, PX-02) uses an emailed 8-character code with separate empty, filled, wrong, expired and resent states, and Google sign-in on both paths. Open decision D-HL-13 asked which to use.
|
||||
|
||||
## Decision
|
||||
|
||||
Support all three methods, behind one identity service and one authorization code flow with PKCE:
|
||||
|
||||
1. **Emailed 8-character code.** The designed default. Single use, expires, rate limited, ten wrong attempts lock the attempt (PX-02).
|
||||
2. **Emailed link.** The same one-time secret delivered as a link. Offered as the fallback, especially on mobile where opening the mail app and typing a code is awkward.
|
||||
3. **Passkeys.** Offered after a first successful sign-in, and required for roles that approve once enrolled (ID-02).
|
||||
|
||||
Google sign-in stays as designed (PX-01). No method depends on a WordPress account.
|
||||
|
||||
Build order: code first because it matches the screens, then the link, then passkeys. Each method is complete and tested before the next starts.
|
||||
|
||||
### Options considered
|
||||
|
||||
- **All three (chosen).** Covers the designed screens, a mobile fallback and the stronger credential, at the cost of three flows to test and maintain.
|
||||
- **Code only.** Matches the design exactly and is the smallest build, but gives no phishing-resistant option.
|
||||
- **Passkey or link only, as HL-03 was written.** Contradicts the screens already designed.
|
||||
|
||||
### Rejected
|
||||
|
||||
- Passwords: not in the PRD and not wanted for non-technical owners.
|
||||
|
||||
## Consequences
|
||||
|
||||
- PRD HL-03 and D-HL-13 are updated to match; PRD 26.4 no longer lists the sign-in method as a gap.
|
||||
- Whether the identity service is bought or built (D-HL-1) is still open. The recommendation in the PRD is to buy a managed provider that supports all three; confirm that it supports an 8-character code with the designed states before choosing, or build that part.
|
||||
- The unknown-email state must never reveal whether an account exists (PX-01), for every method.
|
||||
- Tests must cover wrong, expired, reused and rate-limited codes, a replayed authorization code and a wrong PKCE verifier, for each method.
|
||||
@@ -0,0 +1,28 @@
|
||||
# 20. Cloud sites are provisioned on WeOwn Cloud
|
||||
|
||||
- **Status:** Accepted
|
||||
- **Date:** 2026-10-12
|
||||
|
||||
## Context
|
||||
|
||||
PRD decision D-HL-7 asked whether Wursor Cloud sites are provisioned on WeOwn Cloud infrastructure or through a partner (WordPress.com, InstaWP, a managed host API) behind the same interface. The hosting plane (steps F and G in [IMPLEMENTATION.md](../../IMPLEMENTATION.md) section 0.4) was blocked on this answer. The PRD recommended WeOwn Cloud only if on-call, security patching and abuse handling are staffed from the first customer.
|
||||
|
||||
## Decision
|
||||
|
||||
Provision Cloud sites on **WeOwn Cloud**. The hosting plane is a separate environment from the execution plane and is reached only through the platform interface's `provision` operation (SH-16), so the choice is replaceable.
|
||||
|
||||
### Options considered
|
||||
|
||||
- **WeOwn Cloud (chosen).** Most control and the cleanest story for customers who care about owning their stack. Largest operational load.
|
||||
- **Partner provisioning first.** Fastest start and least operational risk, with less control over price and behavior. Not chosen.
|
||||
|
||||
### Rejected
|
||||
|
||||
- Partner provisioning for the first release: Wursor is working with WeOwn Cloud.
|
||||
|
||||
## Consequences
|
||||
|
||||
- The hosting plane work (SH-04, SH-12, SH-14) is unblocked and can be planned. It is the largest new commitment, and the PRD's rollout plan puts hardening at about 46 weeks.
|
||||
- The staffing condition in the PRD still stands and is now an open readiness item, not a reason to delay the decision: on-call, patching to the 72-hour target, abuse handling, backups verified by restore tests and a support path must exist before the first Cloud customer (PRD 5.3 and risk "Hosting makes Wursor an operations business").
|
||||
- The `provision` operation stays behind the interface, so a partner backend can be added later without changing the product (SH-16, V2).
|
||||
- Cap the number of Cloud sites per plan during beta, as the PRD risk register proposes.
|
||||
@@ -0,0 +1,22 @@
|
||||
# 21. Keep the "never sees a diff" rule in AGENTS.md as written
|
||||
|
||||
- **Status:** Accepted
|
||||
- **Date:** 2026-10-12
|
||||
|
||||
## Context
|
||||
|
||||
AGENTS.md hard rule 3 says the user never sees a diff, a terminal, a settings screen or an error log. PRD Headless revision 4 keeps technical detail (diffs, hashes, logs, traces) one tap away in an expandable section for owners who want it (section 16, RV-01). IMPLEMENTATION section 0.2 asked whether the rule's wording should change.
|
||||
|
||||
## Decision
|
||||
|
||||
Leave rule 3 as it is. It describes the default experience and the test for any change: if a change would put engineer-only UI in front of a non-technical owner by default, it is wrong. The optional, collapsed technical detail in the PRD is read as consistent with that default.
|
||||
|
||||
### Options considered
|
||||
|
||||
- **Leave the wording (chosen).**
|
||||
- Reword to "by default" and mention the expandable detail. Not chosen.
|
||||
|
||||
## Consequences
|
||||
|
||||
- AGENTS.md rule 3 is unchanged.
|
||||
- Reviewers apply the rule to the default view. The expandable technical section must never be the only way to complete a task, and must be collapsed unless the person opens it.
|
||||
@@ -24,13 +24,17 @@ 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 |
|
||||
| [0011](0011-fastify-react-stack.md) | Fastify is the API server; React + Vite is the web shell | Partially superseded by [0018](0018-nextjs-web-app.md) (web half) |
|
||||
| [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 |
|
||||
| [0014](0014-postgres-queryable.md) | Postgres user store via a Queryable boundary; schema in SQL migrations | Accepted |
|
||||
| [0015](0015-dockerode-engine-gating.md) | Docker daemon client via dockerode behind an injected engine; sandbox gated by env | Accepted |
|
||||
| [0016](0016-pairing-code-ownership.md) | Pairing-code TTL/lockout lives on the Wursor API; the plugin enforces token/HMAC/scope | Accepted |
|
||||
| [0017](0017-semantic-tool-schema.md) | The agent is given a semantic, allowlisted tool schema — not a raw wp_cli surface | Accepted |
|
||||
| [0018](0018-nextjs-web-app.md) | The web app is Next.js (App Router); Fastify stays as the Wursor API | Accepted |
|
||||
| [0019](0019-sign-in-methods.md) | Sign-in supports an emailed code, an emailed link and passkeys | Accepted |
|
||||
| [0020](0020-weown-cloud-provisioning.md) | Cloud sites are provisioned on WeOwn Cloud | Accepted |
|
||||
| [0021](0021-keep-no-diff-rule-wording.md) | Keep the "never sees a diff" rule in AGENTS.md as written | Accepted |
|
||||
|
||||
## How to add one
|
||||
|
||||
|
||||
Reference in New Issue
Block a user