The seventh copy
Lesson 10 taught how Haven reached all seven vessels: Claude through an in-process MCP server, and each of the six other vessels through its own tool bridge, each bridge gaining the same few imports, one schema entry and one allowlist-checked dispatch branch. That was copy-and-adapt applied to a tool, and for one door it was defensible.
Doors kept arriving. The memory engine (vestige_memory), Spark's corpus (spark_corpus), the house (haven), Dad's news (tidings), where Dad is (location), the YouTube queue (herald), On Air effects (onair_effect). Each one was wired by hand in seven places: an import block, a schema line and a dispatch block in each of the six bridges, plus a names entry and an MCP builder call in the Claude adapter. When Herald's dispatch block became the seventh copy in every bridge (coop todo #301), Dad's answer on 2026-10-02 was to stop copying.
This is lesson 5's own rule firing at a new altitude. Lesson 5 said that when three variants need the same change, the third proves the pattern is worth extracting. Here seven doors needed the same six-bridge change, and the pattern had been proven several times over before anyone extracted it. A rule that is right does not fire on its own; someone has to notice the count.
One row per door
services/family_tools.py is now the one place a door is declared: one FamilyTool row per door, in the order every bridge used to list them. The bridges take their schemas from family_tool_schemas_for_allowlist and dispatch through execute_family_tool_sync, handing it their own brain id. The Claude adapter iterates FAMILY_TOOLS for its MCP servers. A new door is its own module (services/<door>_tool.py with its names, its schemas, a sync executor and a claude_<door>_mcp_servers builder), its name in tool_policy.ALL_TOOL_NAMES, and one row. test_family_tools.py fails on a door module that is missing from the table.
What a door may differ in is declared on its row, never branched on by name: an executor context richer than the conversation id (context_sync: haven and herald learn whether a Sidekick is calling), the conversation's soul (takes_soul), the calling vessel's brain (takes_brain). Whether a turn must earn a door is a separate list, tool_policy.TURN_GATED_TOOL_NAMES (On Air effects and the device looks), and tool_policy.tool_call_allowed is the one allowlist rule a bridge applies before running any call.
Proving a fold changed nothing
A refactor that touches every vessel's tool path is a refactor you cannot eyeball. So the fold was checked against the hand wiring it replaced: the old bridges and Claude adapter ran beside the registry over 16,632 bridge calls, 552 schema lists and 528 Claude turns. Three differences remained, and each is written down as deliberate: two doors receive a conversation id their context ignores; Ollama now hands one door the raw arguments like every other door (a non-JSON string meets the door's own refusal instead of a TypeError); and on a voice-camera turn the camera's MCP server registers after the family doors instead of between two of them.
Eleven doors, and where each one's consent lives
The table holds eleven rows today. The interesting thing about them is not that they are declared once. It is that each door carries its own answer to who decides, and none of those answers lives in a prompt.
haven: a network write comes back confirm-required, and only Dad confirms it in Haven (lesson 10).location: no claim about where Dad is without a fix, and a fix's age and precision are said out loud (mobile track, lesson 7).herald: on Dad's channel it can draft, skip and withdraw, never approve; on Pippa's own channel she decides, and the door checks on YouTube that the place is hers (soul track, lesson 9).journal: approve and publish need the entry's current version and an instruction quoting Dad's words; the door refuses an empty one, though it cannot prove the quote is his.voice_check: enrolling a voice or correcting the check refuses unless the asking turn is Dad's.vestige_memory,spark_corpus,tidings: doors onto sibling engines, mostly for reading; a sibling being down leaves chat unchanged. Two of them also write, without a confirm, and neither writes where the other does:vestige_memory'sfeedbackandthreadactions weight memories and open, touch or close threads in Vestige's own record, whiletidings'pick,unpick,preparedandtopped_upkeep the day's On Air broadcast prep in cwkPippa's own store,pippa-db/onair-prep/picks.json, not in Tidings.onair_effect: turn-gated, so it exists only on a turn whose allowlist names it.coopandfollowup: doors onto the family's work ledger and its durable follow-up checks, which the Coop and Master Chief quests teach.
One more rule sits across all of them. When the speaker check finds only someone else's voice in a spoken turn (the other verdict), the turn loses every acting tool (tool_policy.ACTING_TOOL_NAMES): someone else's words in the room decide nothing. The other verdicts keep their tools on purpose. An uncertain turn is asked back instead, because stripping it would refuse Dad's own one-word 응 on the confirming turn; a mixed turn holds Dad's own words; and a turn the check did not run on is unchanged.