docs(website): settle the nav interleave and the feature-provider seam
Records Phase 2 PR 8 of the module system: MODULE_SYSTEM.md gains the PR entry in 2.7 and the built-it notes on 1.4 and 1.5; MODULE_API.md 3.3 is amended where building it settled something the draft left open or got wrong. The amendments to 3.3: - The pipeline arrow had role/feature filtering BEFORE admin overrides. The code has always been the other way round, deliberately - the filter runs last so it stays a boundary an override cannot cross (THEMING_AND_NAV 7). - `feature` was documented as public-area-only. It applies in all three areas: core's admin and player navs still carry no flags, but a module row that declares a gate and has it silently ignored is a trap. - How a provider is found: by the module that registered the row, not by a prefix parsed out of the flag name. Core's own rows resolve against the owner id `core`, which core registers useShardFlags under. - What a provider hook returns, and that every unknown fails OPEN. - Why calling one hook per provider in a loop is legal, and why the enumerator is a module export rather than a member of registry. - Six details the interleave settled: unordered rows append rather than defaulting to 0; an ungrouped admin row gets its own trailing group rather than joining core's; a module-created group is a legal override destination; a colliding `to` is dropped with a warning; and why the interleave must precede the override merge. MOD_PATHS' replacement is recorded in both files, including the defect the derivation fixed: the moderator redirect was a third hardcoded list that disagreed with MOD_PATHS about /admin/houses. THEMING_AND_NAV 7's "moderator confinement" note is amended to match. Code: website PR 8 (client-only; 160 client tests, manifest and OpenAPI unchanged), verified with the 7.7 browser smoke. Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -97,6 +97,14 @@ moderator-visible.
|
||||
**Resolved:** nav registration takes a target group and order (`{ group: 'Moderation', order: 30 }`),
|
||||
and `MOD_PATHS` becomes a `roles`-derived computation rather than a path allowlist.
|
||||
|
||||
Built in Phase 2 PR 8. Two things it turned up that this section did not predict. The interleave has
|
||||
to happen **before** the admin-override merge and not after it, because that merge drops any `to`
|
||||
its base array does not declare — appending module rows afterwards would leave them uneditable in
|
||||
Admin → Navigation, which today's UO rows are not. And there was a **third** hardcoded list: the
|
||||
redirect that confines a moderator checked three path prefixes, while `MOD_PATHS` listed five paths,
|
||||
and they disagreed about `/admin/houses` — a moderator who clicked Houses in their own sidebar was
|
||||
bounced straight back to Moderation. One derivation cannot disagree with itself.
|
||||
|
||||
### 1.5 The public nav's feature-gating mechanism is itself a shard system
|
||||
|
||||
Ten of the sixteen entries in `SiteHeader.jsx`'s NAV carry a `feature:` key (`status`, `champs`,
|
||||
@@ -108,6 +116,12 @@ Extracting the module removes the provider that core's own nav filter depends on
|
||||
registers its `useShardFeatures` for its own namespace. No core nav item carries a `feature` today,
|
||||
so with no module installed the filter is a correct no-op.
|
||||
|
||||
Built in Phase 2 PR 8, and core registers into it **now** rather than at extraction: `useShardFlags`
|
||||
goes in under the owner id `core`, so the ten rows above are already resolved through the seam and
|
||||
`SiteHeader` runs one mechanism instead of two. Which provider answers a row is decided by the
|
||||
module that registered it, not by a prefix parsed out of the flag name, so those ten keep the exact
|
||||
strings they carry today and Phase 3 moves them without a rename.
|
||||
|
||||
### 1.6 There is no migration system to model a module migration runner on
|
||||
|
||||
`server/db/schema.sql` is a single idempotent file — 1,380 lines, 67 tables — replayed in full on
|
||||
@@ -627,6 +641,43 @@ fixed enum because the leg set is whatever has been registered.
|
||||
filesystem-conditional static mount, not API surface, for the same reason `/uploads` and `/brand`
|
||||
are not in the manifest.
|
||||
|
||||
- **PR 8** — the nav half PR 7 deferred, and the two seams §1.4 and §1.5 asked for: `withModuleNav`
|
||||
(`client/src/modules/nav.js`) interleaving module rows into core's three navs, `MOD_PATHS` and the
|
||||
moderator redirect replaced by a `roles`-derived computation in `client/src/lib/adminNav.js`, and
|
||||
the generic feature-provider seam (`modules/features.jsx` + `modules/featureGate.js`) that core
|
||||
registers its own `useShardFlags` into. Four decisions, all recorded in
|
||||
[`MODULE_API.md`](MODULE_API.md) §3.3.
|
||||
|
||||
**The interleave happens before the admin-override merge**, which is the decision the rest follow
|
||||
from: the merge is keyed by `to` and drops any key its base array does not declare, so module rows
|
||||
appended after it would be unorderable, unrelabellable and unhideable — and today's UO rows are
|
||||
all three of those things, so appending would make the extraction a visible regression for every
|
||||
operator who has ever edited their nav. Doing it first means a module row is an ordinary row to
|
||||
everything downstream: nothing in `navOverrides.js`, `NavEditor.jsx` or the layouts knows a module
|
||||
exists. **Moderator visibility derives purely from `roles`**, which moves two rows the old
|
||||
allowlist withheld — Dashboard, whose `roles` had always named moderator, and My Characters, which
|
||||
is ungated self-service — both toward what the server already permitted. **A row's `feature` is
|
||||
resolved by the provider its own module registered**, so the namespace comes from the registration
|
||||
rather than from a parsed string prefix. And **core registers through the same seam**, under the
|
||||
owner id `core`, so `SiteHeader` holds one mechanism instead of two and Phase 3 is a deletion.
|
||||
|
||||
The PR also fixed a defect that predates the module system: the moderator redirect was a **third**
|
||||
hardcoded list, and it disagreed with `MOD_PATHS` about `/admin/houses`, so a moderator who
|
||||
clicked Houses in their own sidebar was bounced back to Moderation. The derived allow-list is
|
||||
computed from the **base** nav rather than the merged one, so an override — which is presentation
|
||||
— cannot move that boundary in either direction.
|
||||
|
||||
**933 server tests** (unchanged — this PR is client-only) and **160 client tests** (+37) pass;
|
||||
`routes.manifest.json` is unchanged at 230 routes and the OpenAPI spec regenerates byte-identical.
|
||||
The [§7.7](MODULE_API.md#77-the-client-half-has-to-be-verified-in-a-browser--the-timing-bug-no-test-could-see)
|
||||
browser smoke was re-run, since this is the seam that rule exists for: a throwaway module
|
||||
registering nav in all three areas and a provider granting one flag and withholding another. It
|
||||
confirmed, in Chrome with the console open, that the row lands inside core's Moderation group
|
||||
rather than in an appended block, that the withheld row does not render while the granted one
|
||||
does, that a moderator reaches both `/admin/houses` and the module's own admin page, and that an
|
||||
admin can relabel a module row in Admin → Navigation and have it persist and apply — the whole
|
||||
point of merging before the override layer. Zero CSP reports, zero console errors.
|
||||
|
||||
**Phase 3 — Extract `module-uo`.** Moves out of `website/`: the 8 model directories and their 25
|
||||
tables; the nine UO `utils/` files plus `newsGump.js`; the 13 router/controller files;
|
||||
`scripts/importSpawnAtlas.js` and `db/spawnAtlas.art.json`; `usersShard.controller.js` **minus
|
||||
|
||||
Reference in New Issue
Block a user