docs(website): record slice 2, and what the browser changed about it

The contract as written had core branch on hasExtension to decide about its own
decoration around a slot. That is right when nothing is installed and wrong when
something is installed and fails: the slot IS filled, so the separator renders,
and the component then throws into the boundary and leaves the separator behind
on its own. Core decorates through <Slot wrap> now, inside the boundary, and
hasExtension is gone rather than kept as a trap for the next caller. Every unit
test passed both before and after -- the 7.7 browser smoke is what saw it.

Also recorded, neither a defect: core's own fill occupies a slot, so a module
cannot fill either one until the client half deletes core's (worth stating,
because a module written against 1.2.0 today cannot use them); and core's UO
sections on the user-detail page now fail to load, which is slice 1 removing the
routes rather than anything this slice did.

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
2026-08-11 16:46:11 -05:00
parent 262c86ad4f
commit 96b364b0bc
2 changed files with 50 additions and 3 deletions

View File

@@ -782,6 +782,19 @@ those props — or **nothing at all** when the slot is unfilled. An instance wit
therefore renders exactly what it renders today, which is the same "untouched path" guarantee
`withModuleNav` makes for nav.
**Core decorates a slot with `<Slot wrap>`, never by asking whether it is filled.** `wrap` is called
with the extension's element and rendered *inside* the boundary, so core's own markup around an
extension — a separator, a heading, a rule — shares the extension's fate:
```jsx
<Slot name="site.footer.status" linkStyle={LINK_STYLE} wrap={(link) => <>&nbsp;·&nbsp;{link}</>} />
```
There is deliberately no `hasExtension` for a layout to branch on. Branching is right about the
unfilled case and wrong about the failed one: the slot *is* filled, so the separator renders, and
the component then throws into the boundary and leaves the separator behind on its own. Found in a
browser with this exact footer separator, which is the only place it could have been found.
The slots in 1.2.0:
| Slot | Rendered in | Props core passes |
@@ -830,10 +843,15 @@ already uses for the feature seam. It means the mechanism is exercised by core's
moment it lands, and the extraction becomes a deletion rather than a rewrite made under extraction
pressure.
`declareSlot` and `hasExtension` are module exports of `modules/registry.js` and deliberately **not**
**While core fills a slot, no module can** — first fill wins, and core registers first. In 1.2.0 that
means both slots are occupied and a module's fill is rejected, loudly, naming `core`. This is
scaffolding, not the steady state: core's two fills and the two files behind them are deleted by the
client half of Phase 3, in the same change that registers the module's. Neither slot is one core
intends to fill permanently, and a slot core *does* intend to fill is a slot that should not exist.
`declareSlot` and `extensionFor` are module exports of `modules/registry.js` and deliberately **not**
members of the `registry` object handed to modules, for the same reason `featureProviders()` is not:
declaring is core's, and a module asks about a slot it knows the name of rather than enumerating what
everyone else filled.
declaring is core's, and so is reading back who filled what.
---