Beach Contribution Policy
How contribution works
Beach operates on a tighter governance model than most open-source projects. The repository is not open for pull requests by default. Contributors request access; access is granted once the maintainers are satisfied that the contributor understands and accepts the principles in this document as firm constraints, not guidelines.
All pull requests against Beach core packages must be approved by one of a small team of four senior developers before merging. Agreement with the principles is a condition of access, not a courtesy; a contributor who later argues that an exception is warranted will find that it is not.
This model exists for one reason: the invariant described below is the whole value of Beach. Governance that is looser than the invariant is strict would erode it.
The invariant
Beach's architectural value is a single property: the interior of a Beach application never sees a protocol-specific concept. Inbound adapters translate external protocols into internal events. Outbound plugins translate internal results into external protocols. The event router, the manifest registry, participants, and all event and result types are protocol-agnostic. Neither edge sees the other.
This property — called the invariant — is what makes the durability promise true. When a new protocol arrives, an adapter is mounted. The interior does not change. If the interior ever sees protocol-specific concepts, the durability promise is broken and Beach is no longer Beach.
The invariant is not enforced by tooling. It is enforced by the approval team refusing changes that violate it.
What "protocol-leaking" means
A change leaks a protocol concept into the interior if it allows any part of the interior to know anything about which upstream channel delivered an event or which downstream channel will receive a result. This is broader than it sounds. It is not only named protocols that leak — any upstream channel identity getting into the interior is a leak, because the moment it does, participants start specialising for it.
The test is not "does this field mention MCP by name?" The test is: "after this change, can a participant, the router, or the manifest registry distinguish where an event came from or where a result is going?" If yes, it leaks. Even abstract channel metadata — synchronous vs asynchronous, streaming vs batched, real-time vs queued — is a leak if it originates from upstream channel identity rather than from the event's own semantics.
The reason this matters in practice: once any upstream channel information reaches the interior, participants begin writing code that conditions on it. An A2UI provider who knows that a session is serving a WhatsApp channel will write WhatsApp-specific handling in what should be a channel-agnostic participant. That is the exact failure mode Beach exists to prevent. The line has to be held at the inbound adapter; nothing beyond it.
A change leaks if it introduces any of the above into:
- The event router or its configuration
- The manifest registry or manifest types
- Any participant interface or base class
- Any event type or result type in
@cool-ai/beach-coreor@cool-ai/beach-session - Any routing rule or filter
Examples that fail
- Adding an
mcpSessionIdfield to an event type - Making the router aware of which transport delivered an event (
event.transport === 'sse') - Adding A2A-specific fields to a manifest entry
- A participant checking the protocol of its caller to decide how to respond
- Routing based on
Content-Typeor any other HTTP concept rather than event type - A result type with a
restStatusCodefield - An event metadata field indicating whether the upstream channel is synchronous or real-time — even without naming the protocol, this is upstream channel identity leaking inward
- Any field, flag, or convention that would cause a participant to behave differently depending on which adapter produced the event
Examples that pass
- A new inbound adapter that translates A2A requests into existing event types
- A new outbound plugin that serialises existing result types into GraphQL responses
- A new participant that handles a new event type and produces results using existing result types
- A new event type with protocol-agnostic fields that any adapter could produce
- A new registry entry (part type, turn state, auth method) with no protocol-specific semantics
Why individually-reasonable requests are refused
Most protocol-leaking requests are locally reasonable. A feature that works better if the router knows which transport delivered the event is a real feature that solves a real problem. The request is refused not because the feature is wrong but because accepting it starts a process that does not stop. Beach's value is that it has no such exceptions. The first exception is the one that matters most to refuse.
When a request is refused on invariant grounds, the refusal will cite this document and explain which specific aspect of the invariant would be violated. That is not a dismissal of the request; it is an invitation to find a solution that achieves the same goal at the edge rather than the interior.
What this policy covers
This policy applies to all changes to @cool-ai/beach-core, @cool-ai/beach-session, @cool-ai/beach-transport (the adapter and plugin interfaces only — not specific adapter implementations), and any future package that forms part of the Beach interior.
It does not apply to specific adapter implementations (@cool-ai/beach-mcp, @cool-ai/beach-a2a, etc.), specific plugin implementations, or consumer applications. Those packages exist precisely to carry protocol-specific knowledge at the edge, and protocol concepts belong there.
Questions about where the boundary lies
If you believe a change is being refused incorrectly — that it does not in fact introduce a protocol-specific concept into the interior — raise the question with a specific argument about which boundary the change does or does not cross. The approval team will respond with a specific answer. "This is the policy" is not an acceptable response to a good-faith question about where the boundary lies. The principles are firm; their application to a specific case is always open to discussion.