Look at the diff
When Family Council shipped on 2026-04-10, the diff against the day before showed a new backend/council/ package, new frontend/src/components/council/ components, a hook, some types, and three lines in backend/app/main.py registering the router. No changes to backend/adapters/, backend/routes/chat.py, backend/store/conversations.py, or any variant directory. Council called each brain's chat route the way any client does.
Why that mattered
It proved that an entirely new feature surface could stand on the canonical brain endpoints without disturbing them. Multi-party orchestration is about the hardest case there is; if it can live above the ancestor, most things can.
What happened next
The zero did not last, and the way it ended is the more useful half of the lesson. Today the canonical chat route carries three optional request fields, and council is why each one exists:
system_prompt_override(2026-04-16, for Roleplay Council): the caller supplies the whole system prompt and the route skips the vault, RAG, and presets, so a persona can run on the base model with no Pippa identity underneath.internal_viewer_override(2026-04-27): council reaches each brain with a localhost call of its own, and that call does not carry the identity of whoever started the council. This field forwards the originating viewer, and the route honors it only on localhost-internal calls.system_prompt_addendum(2026-05-21): extra system-only context, never persisted as a Dad row. It is where the table rides now (lesson 2).
Two council-only branches sit beside them: a tag extractor that turns a <roleplay_council> block in Pippa's reply into a council, and a continuation note for a conversation Dad picked out of a council to carry on 1:1. Each field is ignored when absent and each branch fires only when council triggers it, so a plain 1:1 chat behaves exactly as it did before any of them.
The third field is the telling one. Zero touch had a price: with no way to reach the system prompt, all of council's context had to travel in the user message, and the user message is the part that gets saved. Every seat's log filled up with council transcripts filed as Dad's words. Staying at zero was quietly corrupting the ground truth that zero was meant to protect, and a six-line optional field on each chat route fixed it.
Zero touch was never a diff to brag about; it is a dependency test. If a new feature attaches only by editing existing routes and adapters, first suspect that it knows too much about the ancestor's business. Council passed because it used the ordinary conversation API like a client and owned only its own orchestration state.
Nor should you protect the number zero by working around a wrong contract. When several downstream features grow the same hack for the same hole, the ancestor contract really is missing something. Then fix the root, and show that you did not dress up one variant's convenience as a common need.
The payoff shows in rollback. Pull out the council package and its routes, and 1:1 chat, JSONL, and the brain adapters must keep running, with the three fields sitting unused. An additive feature that fails without taking the house down with it is what the rule means in practice.