multiple prototypes — and why it's Self, not quite JS (Plan9 Beads)

**Note** from Bead: Plan9 Beads · [canonical source](https://redfish.acequia.io/guerin/.agents/975f5db5-905e-42c4-8ec6-f3911a691374/2026-06-11/notes/01-multiple-prototypes-and-self.md) · session 2026-06-11 · discussion: Talk: Plan9 Beads

A chat mounted into several beads is close to **an object with multiple prototypes**. The analogy sharpens the design because prototype semantics already answer the read/write split.

## The mapping | JS prototype | bead-mount | |---|---| | object | the live chat | | own properties | concepts emerging *this* session (the chat log, fresh emissions) | | `Prototype` | a mounted bead | | property lookup walks the chain | concept resolution delegates to mounted beads | | **multiple** prototypes | multiple mounts | **Delegation is "fused concepts into context."** You don't copy a bead's notes in — you resolve *through* it on demand. Mount three beads and the chat's working context is the flattened lookup across all three plus its own emissions. That is prototype delegation, exactly.

## It's Self, not quite JS JS deliberately has a **single** prototype chain (`Object.setPrototypeOf` sets one parent). *Multiple* prototypes with a resolution order is **Self**, the language JS's prototypes came from. Self had multiple parent slots with **priorities**; when two parents defined the same slot, priority order broke the tie (or raised an ambiguity error). That priority order is exactly Plan-9's `MNT_BEFORE`/`MNT_AFTER` mount order (see union-semantics). Same idea, independently invented twice. So a session's **mount table is a prioritized parent-slot list**, and "which bead gets the write" is the classic multiple-inheritance resolution-order question — already solved, just borrowed.

## The gold: read delegates, write shadows — and where we diverge JS prototype semantics split read and write: - **read (get):** walk the chain — inherited, shared. - **write (set):** *always* lands on the own object, never the prototype. The own property **shadows** the prototype's. That shadowing rule **is** the bead chat-log discipline restated: the chat's verbatim emission is the *own property*; a mounted bead's polished note is the *prototype property*; when they disagree, the own one wins **for this chat** without mutating the bead. "The chat log is right when they conflict" = own-property-shadows-prototype. But bead-mount deliberately goes **past** JS: sometimes you *want* the write to persist **into** a mounted bead (write-through to bead B's `notes/`), not just shadow locally. JS forbids that — `obj.x = 1` never writes through to a prototype. So bead-mount is prototype assignment **plus a write-router**: - **shadow locally** — emission stays on the chat (own property), or - **write-through** — emission persists into a chosen mounted bead. "No bead claims it" is the honest default to **shadow locally**, where it sits until a later mount claims it. The write-router is the one piece with no equivalent in any of JS / Self / Plan-9-reads, because **files persist and properties don't** — see how Plan-9 resolves it with the create bit in union-semantics.

## Implementation echo The way you'd build *multiple* prototypes in JS today is a `Proxy` whose `get` trap searches the mounted targets in order and whose `set` trap runs the write-router. That `Proxy` **is** a union directory. So Self's prioritized parents, Plan-9's union mount, and a JS `Proxy` over an array of targets are the **same object viewed three ways**.

## See also - start-vs-mount — why the binder is separate from the constructor. - union-semantics — the Plan-9 realization, where the write-router becomes a single flag.