docs: record the five Phase 8 prerequisites, and report contributions
Bring the living documentation in line with what the five passes actually shipped, and correct the claims they superseded rather than leaving them to mislead: the module UI context is six members, not five; module styling is compiled CSS, not inline objects only; the strict manifest is version 7. Also close a silent failure the new contribution mechanism introduces. A contribution whose target capability nobody provides — or whose provider moved outside its declared range — is not published, and the manifest is valid and the artifact exists, so an operator sees a screen that renders nothing with no error anywhere. `pnpm modules:validate` now lists each declared contribution with its capability and range and says whether a compatible provider is installed, and Doctor's discovery check reports the same per module. Both derive from the publication registry the API serves from, so neither can disagree with what a running platform would actually publish. Co-Authored-By: Claude Opus 5 <[email protected]>
This commit is contained in:
co-authored by
Claude Opus 5
parent
ca3f37d5f9
commit
20d5128bad
@@ -289,7 +289,8 @@ artifacts cannot drift.
|
||||
externalizing it would make every host supply a compatible React — defeating standalone hosting. The
|
||||
accepted cost is duplicate React runtimes and large unminified artifacts (~570 KB for the template);
|
||||
sharing one runtime later is a build optimization that does not change the contract. Styling is React
|
||||
inline style objects only: a stylesheet import **fails the build** rather than emitting a sidecar
|
||||
inline style objects only at that checkpoint (Phase 8 pass 2 replaced that with compiled
|
||||
stylesheets): a stylesheet import then **failed the build** rather than emitting a sidecar
|
||||
`.css` that no manifest declares and no host loads. Building is an authoring step — Core never builds,
|
||||
and installing a module or starting the API never triggers one.
|
||||
|
||||
@@ -488,7 +489,8 @@ reported `blocked · configuration_missing · bucket` — never imported, never
|
||||
from inside its own `start` hook — and its required dependents block behind it through ADR 0028's
|
||||
existing mechanism. Doctor and `pnpm modules:validate` report the same thing while still importing no
|
||||
module code. That change introduced `manifestVersion: 5` as a hard cutover; the current strict
|
||||
manifest is version 6 after ADR 0033 added the optional display declaration.
|
||||
manifest is version 7 after ADR 0033 added the optional display declaration and ADR 0041 added
|
||||
browser contributions.
|
||||
|
||||
**Ekklesia does not store secrets, and does not pretend to.** There is no encryption at rest here and
|
||||
no key management, so a `secret` key's value is supplied by the deployment
|
||||
@@ -792,6 +794,74 @@ unproven launch-blockers rather than existing guarantees. Read
|
||||
[Security model and deployment trust boundaries](docs/development/security.md) before installing a
|
||||
module or exposing the API.
|
||||
|
||||
**Phase 8 adds the five generic platform prerequisites the first production feature module needs.**
|
||||
None of them is a feature module, and none of them names one.
|
||||
|
||||
Phase 8 pass 1 gives every Ekklesia screen one design vocabulary
|
||||
([ADR 0039](docs/architecture/decisions/0039-shared-design-tokens-and-elements.md)).
|
||||
`@ekklesia/design-tokens` is a validated, enumerable registry of **semantic** tokens whose category
|
||||
and `--ekklesia-*` identifier are derived from the token's name, and whose values must be bounded
|
||||
literals — so a registry that constructs can be emitted as CSS without escaping, and a future theme
|
||||
editor has something labelled, described, and categorised to enumerate. `@ekklesia/ui` is a small set
|
||||
of foundational elements (button, inputs, field, panel, dialog, tabs, table and list primitives,
|
||||
badge, toast, and empty/error/loading states), every value a token reference, with real
|
||||
`:focus-visible` treatment and status that never signals by colour alone. `pnpm boundaries` opens
|
||||
exactly two shared roles to modules by name and pins both design packages beneath every other role.
|
||||
The Dashboard and display install the defaults at their document root; custom properties inherit into
|
||||
module artifacts, so a module writes `var(--ekklesia-…)` with nothing to import at run time. **No
|
||||
theme editor, brand builder, second theme, or component catalogue exists.**
|
||||
|
||||
Phase 8 pass 2 gives module UI real stylesheets and gives every browser artifact its own root
|
||||
([ADR 0040](docs/architecture/decisions/0040-module-stylesheets-and-artifact-isolation.md)). A module
|
||||
imports plain `.css` and the canonical builder compiles it **into** the one declared artifact as
|
||||
text — no sidecar can exist, which is the failure ADR 0023 refused stylesheets to avoid. The build
|
||||
rejects a module stylesheet that *declares* an `--ekklesia-*` variable or leaves an unresolved
|
||||
`@import`. The generic loader then mounts every artifact — primary, display, and contributed — inside
|
||||
its own open Shadow DOM root with its stylesheet installed there, so one module's rules cannot reach
|
||||
the Dashboard or another module and theirs cannot reach in. That root is a correctness boundary for
|
||||
styles and **is not a sandbox**: module code remains trusted, same-realm code. The browser mount ABI
|
||||
is versioned with no dual acceptance; migrating is one `pnpm build`.
|
||||
|
||||
Phase 8 pass 3 lets one module contribute a browser renderer to a capability another module provides
|
||||
([ADR 0041](docs/architecture/decisions/0041-module-browser-contributions.md)) — the browser half of
|
||||
the capability system Core has had since Phase 1. `manifestVersion: 7` declares a namespaced id, its
|
||||
own version, a target capability, a compatible range, and one artifact, and nothing else: there is no
|
||||
payload schema, slot, ordering, or rendering hint anywhere, so Core cannot learn what any of it
|
||||
means. Publication requires that another eligible module provides the capability inside the declared
|
||||
range; a contribution that fails is absent rather than broken. A consumer discovers through
|
||||
`ModuleUiHostContext.contributions` and mounts by id, never by building a publication URL, and can
|
||||
only see contributions aimed at a capability it itself provides. A contributed renderer receives its
|
||||
own id and the consumer's opaque JSON input — no API client, realtime, principal, configuration,
|
||||
database, storage, or credential — and loading one grants its module nothing.
|
||||
|
||||
Phase 8 pass 4 closes the object-storage write path
|
||||
([ADR 0042](docs/architecture/decisions/0042-bounded-streaming-request-bodies.md)). A module route
|
||||
declares `bodyMode`: `buffered` (the default, and what every route already meant) keeps the 1 MiB
|
||||
ceiling and materializes the body before the handler runs; `streamed` raises it to 32 MiB and counts
|
||||
bytes as the handler consumes them, holding nothing. 32 MiB is also the platform's absolute ceiling
|
||||
in every mode — sized for one 25 MiB image plus bounded overhead, one file per request, with no
|
||||
unbounded option anywhere. The platform's own limiter replaces the buffering dependency: a declared
|
||||
oversize is refused before authorization, the buffered path now counts what arrived rather than
|
||||
trusting a header, and a streamed overflow errors the stream, cancels the source, and replaces the
|
||||
response with the same sanitized `413` even if the handler swallowed it. Signed write URLs,
|
||||
resumable uploads, quotas, and anything above the ceiling remain deliberately absent.
|
||||
|
||||
Phase 8 pass 5 gives the display host-owned offline resilience for **the shell and the declared
|
||||
code** ([ADR 0043](docs/architecture/decisions/0043-display-offline-code-fallback.md)). A service
|
||||
worker scoped to `/display/` caches the page, the host script, the module's display artifact, and
|
||||
every contributed renderer that display mounted, network-first so a screen can never be trapped on
|
||||
obsolete code. It caches **nothing else, ever**: not the descriptor, `/api/v1/principal`, any API
|
||||
response, the realtime stream, a pairing exchange, or any cross-origin request. The consequence is
|
||||
stated rather than papered over — a display cannot render module content while offline, because
|
||||
mounting needs an authorization-bearing descriptor that is never cached, so a revoked device stays
|
||||
revoked and cached code can never manufacture authorization. What it buys is that a kiosk rebooting
|
||||
during an outage boots into Ekklesia's own offline state and recovers by itself, and that
|
||||
reconnecting mounts with nothing left to download. **Module-owned offline content and media remain
|
||||
the module's responsibility and do not exist yet.**
|
||||
|
||||
Ekklesia Lite, a theme editor, a module store or installer, a standalone host, and every feature
|
||||
module — Signage included — still do not exist, and nothing in Phase 8 claims otherwise.
|
||||
|
||||
## Quick start
|
||||
|
||||
Prerequisites: Node.js 24 LTS, pnpm 11.12, and Docker with Compose.
|
||||
|
||||
Reference in New Issue
Block a user