"Sharing a process is no excuse to skip the contract. But a contract and HTTP are not the same thing."
The Tempting Shortcut
Bonfire's built-in UI and engine run close together. A browser UI can talk to the server's HTTP API, while tests or another shell may call the engine in-process. That proximity creates a temptation: import the internal model or repository directly because it is visible in the same codebase. The code is short and appears to work.
The problem is not every direct function call. The problem is which function the client knows. If the UI depends on internal types or storage, an engine refactor can break it even though no public contract changed. The model also travels through a private path no other client can use. If clients instead depend on a shared request-and-response port, the transport can vary: an HTTP adapter in the browser, an in-process adapter in tests or a same-process shell.
Design It Like Remote; Choose the Transport
The precise discipline is this: clients depend on the public contract, never engine internals. Define requests and responses that would still make sense across a machine boundary, then attach the transport that fits the deployment. Bonfire's browser UI crosses HTTP and proves that surface is sufficient. That does not require every in-process caller to pay for HTTP. The invariant is one contract, not one transport.