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
@@ -1232,7 +1232,7 @@ its role-specific URL only from the protected descriptor below.
|
||||
|
||||
## Module display host
|
||||
|
||||
Manifest version 6 adds one independently optional display declaration
|
||||
The manifest carries one independently optional display declaration
|
||||
([ADR 0033](../architecture/decisions/0033-display-kiosk-host-contract.md)):
|
||||
|
||||
```json
|
||||
|
||||
@@ -137,7 +137,8 @@ enforces those permissions while the module is disabled, because it mounts no ro
|
||||
|
||||
## Module permission composition
|
||||
|
||||
A module manifest's `permissions` array (`manifestVersion: 4`,
|
||||
A module manifest's `permissions` array (introduced in `manifestVersion: 4`; the current strict
|
||||
manifest is version 7 — see
|
||||
[Module development](../module-development/README.md#canonical-manifest)) declares permissions in
|
||||
the exact same `PermissionDeclaration` shape — but declaring one there only proves it is
|
||||
well-formed and correctly namespaced under the module's own id; it does not, by itself, register
|
||||
@@ -667,7 +668,7 @@ module database usage), and exactly one `pool.end()` call regardless of how many
|
||||
- `packages/contracts/tests/authorization.test.ts` — `PermissionDeclaration` shape validation:
|
||||
every baseline, unknown-property/missing-baseline/invalid-baseline/blank-or-oversized-description
|
||||
rejection, malformed permission id rejection.
|
||||
- `packages/contracts/tests/module-manifest.test.ts` — `manifestVersion: 4` acceptance and version
|
||||
- `packages/contracts/tests/module-manifest.test.ts` — exact-version acceptance and version
|
||||
2/string-array rejection, per-declaration validation (mirroring the contracts-level cases above),
|
||||
module-namespace enforcement, duplicate permission id at its exact `permissions[index].id` issue
|
||||
path, and default-empty-list isolation across separate parses.
|
||||
|
||||
@@ -37,7 +37,10 @@ the root verification command discovers them automatically.
|
||||
|
||||
`pnpm modules:validate` builds the workspace, then scans `modules/*` without a central registry. A
|
||||
missing or empty modules directory is valid and reports zero installed modules. Installed modules
|
||||
must produce the compiled JavaScript entrypoints declared by their static manifests. Optional
|
||||
must produce the compiled JavaScript entrypoints declared by their static manifests. Each module's
|
||||
declared browser contributions are listed with their target capability and range, and marked
|
||||
according to whether a compatible provider is installed — a contribution nobody provides is otherwise
|
||||
silent, since the manifest is valid and the artifact exists while nothing renders (ADR 0041). Optional
|
||||
schema entrypoints are loaded and validated for table ownership, namespacing, duplicates, and
|
||||
foreign-key isolation; this command does not connect to PostgreSQL or run migrations.
|
||||
|
||||
@@ -82,7 +85,8 @@ not a broken deployment).
|
||||
Doctor reports a fixed, deterministic set of checks:
|
||||
|
||||
- The active Node.js version against the repository's declared engine range.
|
||||
- Static module discovery and dependency-first ordering.
|
||||
- Static module discovery and dependency-first ordering. A module that declares browser
|
||||
contributions also reports how many of them have a compatible provider among the installed set.
|
||||
- Schema discovery, including validated module and table counts. This imports compiled schema
|
||||
definitions, which the module contract requires to perform no configuration reads, I/O, or
|
||||
database connection.
|
||||
|
||||
@@ -201,7 +201,7 @@ separate deployment and threat-model decision.
|
||||
|
||||
### A display page is authorized; its artifact bytes are not authority
|
||||
|
||||
Manifest version 6 binds a display artifact to one permission declared by the same module
|
||||
The manifest binds a display artifact to one permission declared by the same module
|
||||
([ADR 0033](../architecture/decisions/0033-display-kiosk-host-contract.md)). The declaration rejects
|
||||
a `public` baseline: the current display host requires an ordinary authenticated cookie principal,
|
||||
and public/device identity is not approximated with an anonymous exception. Both the stable
|
||||
|
||||
@@ -69,6 +69,77 @@ protocol-relative, backslash-prefixed, and scheme-bearing values cannot become n
|
||||
Modularity checks keep authentication out of the module UI SDK/context and Dashboard login code out
|
||||
of the separate display bundle.
|
||||
|
||||
Phase 8 adds evidence for the five platform prerequisites.
|
||||
|
||||
**Design tokens and shared elements** (ADR 0039). The token registry's tests prove it is
|
||||
deterministic and enumerable, that every token derives a unique `--ekklesia-*` identifier from its
|
||||
name and carries a label, description, and category, that each status family pairs a colour with a
|
||||
non-colour symbol, and that a duplicate name, an unknown category, an invalid or single-segment name,
|
||||
and a value carrying a declaration terminator, a rule block, a comment, an at-rule, `url(`,
|
||||
`!important`, an escape, a control character, or markup are each rejected — with an error that never
|
||||
echoes the rejected declaration. The elements' tests assert accessible structure (label/hint/error
|
||||
association, icon-button naming, tab roles and arrow-key movement, announced error and loading
|
||||
states), that status never depends on colour alone, that a dialog renders in place rather than
|
||||
portalling out of an isolated root, and — mechanically — that the stylesheet and every component
|
||||
source contain no literal colour, font stack, or theme value and reference only registered tokens.
|
||||
Both packages compile module-shaped source under the exact browser project a module uses, where
|
||||
`process.env` must fail to typecheck, so the browser boundary is measured rather than asserted.
|
||||
|
||||
**Module stylesheets and artifact isolation** (ADR 0040). The builder's tests prove a plain
|
||||
stylesheet is compiled into the declared artifact with no `.css` emitted beside it, that a local
|
||||
`@import` is inlined, and that an unresolved `@import`, a stylesheet declaring an `--ekklesia-*`
|
||||
variable, and a non-CSS dialect are each rejected with their own sanitized code — while a private
|
||||
alias derived from a registered token is accepted. The loader's tests prove every artifact mounts
|
||||
inside an open shadow root, that the artifact receives that root rather than the host's element, that
|
||||
exactly one `<style>` is installed per mount and never accumulates across remounts, that the host's
|
||||
base rules precede the artifact's so a display can override `:host`, that unmount removes the whole
|
||||
root even when the module's cleanup throws, that a failed mount leaves neither DOM nor styles behind,
|
||||
that an environment without Shadow DOM fails closed rather than mounting unisolated, and that an
|
||||
artifact built against the previous ABI is refused with `MODULE_UI_ABI_UNSUPPORTED`. The template's
|
||||
suite builds real React artifacts through the canonical pipeline and asserts the compiled stylesheet
|
||||
travels inside the one declared file.
|
||||
|
||||
**Browser contributions** (ADR 0041). Manifest tests cover namespacing, the single local segment,
|
||||
duplicate ids and artifact paths, collision with an entrypoint, and a contribution targeting a
|
||||
capability its own module provides. Discovery tests prove a contribution artifact inherits the exact
|
||||
path, symlink, containment, extension, and file-kind protections every other compiled entrypoint has,
|
||||
including two contributions linked to one file. Publication tests prove a contribution is published
|
||||
only when another eligible module provides its capability *within the declared range*, that the
|
||||
contributing module never sees its own contribution, that a lookup fails closed on an unknown,
|
||||
mismatched, or non-string identity, and that the published record carries no domain vocabulary at
|
||||
all. Route tests prove the artifact is served byte-for-byte at a URL that cannot select a neighbour,
|
||||
and that an unknown id, another module's id, a traversal, a source map, a wrong version, an absent
|
||||
provider, and an incompatible provider all collapse into one indistinguishable `404`. The loader's
|
||||
tests cover mount, update-without-remount, a renderer that declares no `update`, deterministic and
|
||||
idempotent unmount, a sanitized update failure, and input that is not genuinely JSON — a `Date`, a
|
||||
function, `NaN`, `undefined`, and a cycle — refused *before* anything is imported and without ever
|
||||
echoing the consumer's data. The flagship shell fixture drives the whole relationship: a `signage`
|
||||
module discovers, mounts, updates, and unmounts an `announcements` React renderer with neither module
|
||||
importing the other, the consumer seeing identity but never a URL, the host resolving publication,
|
||||
and a contribution id the server withheld refused without a fetch.
|
||||
|
||||
**Bounded streaming uploads** (ADR 0042). Contract tests pin the two ceilings, that `maxBodyBytes` is
|
||||
checked against the ceiling the declared mode selects, that 32 MiB holds a 25 MiB image and one byte
|
||||
more is refused, and that no mode admits an unbounded body. HTTP tests cover exact-boundary success,
|
||||
a declared `Content-Length` refused before the handler runs, a misleading smaller `Content-Length`
|
||||
that cannot smuggle a megabyte past the limit, a chunked overflow, a `413` that still wins when the
|
||||
handler swallowed the stream error, proof that the body is genuinely not buffered (a handler sees the
|
||||
first chunk before the last is produced), an aborted upload surfacing as an ordinary failure rather
|
||||
than a `413`, an unread body being released, authorization ordering in both directions, and a
|
||||
rejection that names no limit, size, or body content. The live suite uploads a real 25 MiB body over
|
||||
a real socket end to end and proves the server is healthy immediately afterwards.
|
||||
|
||||
**Display offline fallback** (ADR 0043). The worker's caching logic is exercised directly, because it
|
||||
touches no global: first online load, offline reload after one successful visit, a missing uncached
|
||||
artifact failing honestly, contributed-renderer fallback, version upgrade with pruning that keeps
|
||||
every artifact of the current version and never touches another module's, superseded cache
|
||||
generations removed while unrelated ones survive, network-first behaviour picking up rebuilt bytes,
|
||||
and an explicit inventory proving the descriptor, principal, module API, realtime stream, pairing
|
||||
exchange, auth routes, cross-origin signed object URLs, and the worker itself are **never** stored —
|
||||
so a revoked device's refusal is what a reconnected display sees. Page-level tests cover the quiet
|
||||
offline indicator, a cache fallback reported while the browser still claims to be online, automatic
|
||||
re-resolution when the network returns, and a revoked device dropping back to pairing on reconnect.
|
||||
|
||||
`pnpm verify` is intentionally Docker-free. It never plans or applies migrations against a live
|
||||
database.
|
||||
|
||||
|
||||
Reference in New Issue
Block a user