docs(website): Phase 2 PR 7 — the client chunk's delivery contract #132

Merged
whitlocktech merged 1 commits from docs/module-client-registry into main 2026-08-11 04:01:30 +00:00
Member

Docs half of website#134 — Phase 2, PR 7 of MODULE_SYSTEM.md §2.7: the client registry, window.__rg, the chunk's static mount and the script injection.

MODULE_API.md

§3.1 gains the four constraints PR 7 settled, all normative, all of them the kind that is easy to get wrong and impossible to catch in a unit test:

  • The static root is the entry's directory, never the module root. One express.static over a module root publishes its server source, its module.json and its schema fragment — so an entry sitting directly in the module root is rejected by the loader rather than left to whoever writes the mount. §2.1's client.entry row says so too.
  • The mount sits behind the module's state guard, 503 while startup_failed and 404 while disabled, exactly as its API routes are — the browser must not be running the client half of something the server half has stopped serving. no-cache, because a library build emits an unhashed entry.js.
  • The tag is injected before </body>, not into </head>. Module scripts are deferred and execute in document order, so core's bundle — which publishes window.__rg — has to come first. </head> works today only because Vite hoists core's entry into <head>; that is a bundler's emit decision, and if it changed, every module in the wild would break with nothing in core having been edited.
  • Core renders on DOMContentLoaded, and the readyState check is 'complete', not 'loading'.

§3.4 strikes AdminPage from the UI kit: core has no such component — admin views are plain markup inside AdminLayout — and inventing one to satisfy a table would be a core change with no consumer until Phase 3. The kit ships its seven real members; adding an eighth later is a minor MODULE_API_VERSION bump, which is the case the versioning exists for.

§7.7 is new, and it is the part worth reading. Every unit test passed against a build that did not work in a browser. document.readyState during a deferred script is 'interactive', not 'loading', so core mounted before any module chunk had evaluated: the module's routes were absent from the first render and its URL redirected home — indistinguishable from a module that failed to load, with nothing logged anywhere. The chunk had fetched, executed and registered into a registry nothing read again.

No test in this repo can see that: there is no DOM in either runner, and the ordering being asserted is the browser's rather than the code's. So §7.7 writes the smoke down as a four-step procedure to repeat whenever this seam changes — throwaway module, hand-written ESM entry, MODULES_DIR, real browser with the console open. It also records the two smaller things the same run confirmed: the chunk executes under the enforced script-src 'self' with zero CSP reports (the property §3.6 called the highest-risk detail in the plan), and the shell is read once at boot, so rebuilding the client without restarting the server produces a failure that looks exactly like a module violating the contract.

MODULE_SYSTEM.md

§2.6 step 3 amended to the </body> injection point; §1.14's resolution notes the policy was verified in a browser rather than only reasoned about; §2.7 records PR 7's four decisions and what verified them. PRs 1–7 of 9 done.

BACKEND_DESIGN.md

/modules joins /uploads and /brand as a filesystem-conditional static mount deliberately outside the route manifest — including it would make the output depend on which modules happened to be on the volume of the machine that generated it.


  • AI-assisted: written with Claude Code (Claude Opus 5)

🤖 Generated with Claude Code

https://claude.ai/code/session_018ocYxQWk3EhZe5gWRJXFU8

Docs half of **website#134** — Phase 2, PR 7 of `MODULE_SYSTEM.md` §2.7: the client registry, `window.__rg`, the chunk's static mount and the script injection. ## `MODULE_API.md` **§3.1 gains the four constraints PR 7 settled**, all normative, all of them the kind that is easy to get wrong and impossible to catch in a unit test: - **The static root is the entry's directory, never the module root.** One `express.static` over a module root publishes its server source, its `module.json` and its schema fragment — so an entry sitting directly in the module root is *rejected by the loader* rather than left to whoever writes the mount. §2.1's `client.entry` row says so too. - **The mount sits behind the module's state guard**, `503` while `startup_failed` and `404` while `disabled`, exactly as its API routes are — the browser must not be running the client half of something the server half has stopped serving. `no-cache`, because a library build emits an unhashed `entry.js`. - **The tag is injected before `</body>`, not into `</head>`.** Module scripts are deferred and execute in document order, so core's bundle — which publishes `window.__rg` — has to come first. `</head>` works today *only* because Vite hoists core's entry into `<head>`; that is a bundler's emit decision, and if it changed, every module in the wild would break with nothing in core having been edited. - **Core renders on `DOMContentLoaded`, and the readyState check is `'complete'`, not `'loading'`.** **§3.4** strikes `AdminPage` from the UI kit: core has no such component — admin views are plain markup inside `AdminLayout` — and inventing one to satisfy a table would be a core change with no consumer until Phase 3. The kit ships its seven real members; adding an eighth later is a minor `MODULE_API_VERSION` bump, which is the case the versioning exists for. **§7.7 is new, and it is the part worth reading.** Every unit test passed against a build that did not work in a browser. `document.readyState` during a deferred script is `'interactive'`, not `'loading'`, so core mounted before any module chunk had evaluated: the module's routes were absent from the first render and its URL redirected home — indistinguishable from a module that failed to load, with nothing logged anywhere. The chunk had fetched, executed and registered into a registry nothing read again. No test in this repo can see that: there is no DOM in either runner, and the ordering being asserted is the browser's rather than the code's. So §7.7 writes the smoke down as a four-step procedure to repeat whenever this seam changes — throwaway module, hand-written ESM entry, `MODULES_DIR`, real browser with the console open. It also records the two smaller things the same run confirmed: the chunk executes under the **enforced** `script-src 'self'` with zero CSP reports (the property §3.6 called the highest-risk detail in the plan), and the shell is read once at boot, so rebuilding the client without restarting the server produces a failure that looks exactly like a module violating the contract. ## `MODULE_SYSTEM.md` §2.6 step 3 amended to the `</body>` injection point; §1.14's resolution notes the policy was verified in a browser rather than only reasoned about; §2.7 records PR 7's four decisions and what verified them. **PRs 1–7 of 9 done.** ## `BACKEND_DESIGN.md` `/modules` joins `/uploads` and `/brand` as a filesystem-conditional static mount deliberately outside the route manifest — including it would make the output depend on which modules happened to be on the volume of the machine that generated it. --- - [x] AI-assisted: written with Claude Code (Claude Opus 5) 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_018ocYxQWk3EhZe5gWRJXFU8
wtclaude added 1 commit 2026-08-11 03:55:17 +00:00
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 </body> 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 </body> 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 <noreply@anthropic.com>
whitlocktech merged commit b43b40e71b into main 2026-08-11 04:01:30 +00:00
whitlocktech deleted branch docs/module-client-registry 2026-08-11 04:01:31 +00:00
Sign in to join this conversation.
No description provided.