From 17d8608a7ff17c855432e326e7fa35da54b91a7c Mon Sep 17 00:00:00 2001 From: wtclaude Date: Mon, 10 Aug 2026 22:54:39 -0500 Subject: [PATCH] =?UTF-8?q?docs(website):=20record=20Phase=202=20PR=207=20?= =?UTF-8?q?=E2=80=94=20the=20client=20chunk's=20delivery=20contract?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit MODULE_API.md - 3.1 gains the four normative constraints PR 7 settled: the static root is the entry's directory (an entry in the module root is rejected), the mount sits behind the module's state guard with no-cache, the script tag is injected before so core's bundle runs first, and core renders on DOMContentLoaded with a readyState === 'complete' check - 2.1: client.entry must be in a subdirectory; present-but-empty is rejected - 3.4: AdminPage struck from the UI kit — core has no such component - 7.7 (new): the browser smoke, and the timing bug no test in this repo can see. Every test passed against a build that did not work in a browser MODULE_SYSTEM.md - 2.6 step 3 amended to the injection point - 2.7 records PR 7's four decisions and what verified them; 1-7 of 9 done BACKEND_DESIGN.md - /modules is the fourth filesystem-conditional static mount outside the route manifest, with its root, guard and cache policy stated Co-Authored-By: Claude --- website/BACKEND_DESIGN.md | 13 +++++-- website/MODULE_API.md | 72 ++++++++++++++++++++++++++++++++++++--- website/MODULE_SYSTEM.md | 54 +++++++++++++++++++++++------ 3 files changed, 121 insertions(+), 18 deletions(-) diff --git a/website/BACKEND_DESIGN.md b/website/BACKEND_DESIGN.md index e344f47..032ab13 100644 --- a/website/BACKEND_DESIGN.md +++ b/website/BACKEND_DESIGN.md @@ -735,9 +735,16 @@ slashes from generated path keys — see *Regenerating the spec* in the website domain split makes that necessary. Scope: the manifest keeps `/api/**` and `/.well-known/**` from the public app plus everything on the -internal listener. The SPA catch-all, `/uploads` and `/brand` are filesystem-conditional static -mounts — not API contract, and including them would make the output depend on whether CI had built -the client. +internal listener. The SPA catch-all, `/uploads`, `/brand` and `/modules` are filesystem-conditional +static mounts — not API contract, and including them would make the output depend on whether CI had +built the client, or on which modules happened to be on the volume of the machine that generated it. + +`/modules//` is the last of those and the newest: an installed module's prebuilt client chunk, +served from the directory its `client.entry` sits in and never from the module root, behind the +module's own state guard (`503` when it failed to start, `404` when disabled) and with +`Cache-Control: no-cache`, because Vite's library build emits an unhashed `entry.js`. Anything else +under `/modules` is a `404` rather than the SPA shell. The full contract is +[`MODULE_API.md`](MODULE_API.md) §3.1. A third generated file, `server/routes.guards.json`, is a **review aid and not a contract**: per route, the middleware handler count plus the *named* middleware on its mount chain. It exists because a diff --git a/website/MODULE_API.md b/website/MODULE_API.md index cd35eba..f350220 100644 --- a/website/MODULE_API.md +++ b/website/MODULE_API.md @@ -85,7 +85,7 @@ rejected rather than ignored, so a typo is a loud failure and not a silently-ine | `version` | yes | Semver. Recorded in `installed_modules`; shown on failure. | | `coreApi` | yes | Semver range checked against `MODULE_API_VERSION` (§1.1). | | `server` | no | Entry point, relative to the module root. Absent ⇒ client-only module. | -| `client.entry` | no | Prebuilt ESM chunk, relative to the module root. Absent ⇒ server-only module. | +| `client.entry` | no | Prebuilt ESM chunk, relative to the module root, and **in a subdirectory** — the directory it sits in is what gets served (§3.1). Absent ⇒ server-only module; present-but-empty is rejected, since it claims a client half and delivers none. | | `schema` | no | Idempotent SQL fragment (§2.6). | | `purge` | no | Destructive teardown (§2.6). Required if `schema` is present. | | `mounts` | no | Declared prefixes per tier (§2.3). Declaration is the contract; the loader compares it against what the module actually registers and rejects a mismatch. | @@ -430,17 +430,50 @@ Exactly as `MODULE_SYSTEM.md` §2.6 resolved, and Phase 1's spike is what proves 1. Module CI builds `client/dist/entry.js` with Vite in **library mode**, `react`, `react-dom`, `react-dom/client` and `react-router-dom` declared **external**. -2. Core serves the module directory statically at `/modules//` — same-origin, so +2. Core serves the **directory the entry sits in** statically at `/modules//` — same-origin, so `script-src 'self'` (`config/csp.js:49`) admits it with no nonce and no inline. -3. `utils/htmlShell.js` injects `