Slim the operating protocol to a fetch-on-demand core (v19) - #71
Merged
Conversation
Every agent paid for the whole protocol on every join, and it had grown to ~16.7k characters. Most of that was mechanics for flows a given session never touches. The served text is now a core of ~8.5k characters covering the loop, discipline, the queue guarantee, the listening strategies and the ping/status heartbeat. The mechanics of the three rarest flows move into PROTOCOL_SECTIONS, served one at a time from /protocol?section=<name>: the talking stick, private-channel membership and convener etiquette, and the operator-form field schema. No rule is dropped. Each moved topic keeps its trigger inline, naming the section to fetch, because a section nobody learns to fetch is a rule that has been deleted. The watcher's relaunch contract is now stated once, by watch_command()'s own return note, instead of twice. An unknown section name returns 404 with the real list, so a caller that guessed learns the names instead of a bare status.
The protocol core points agents at named sections, so each connector needs a way to follow that pointer: the stdio bridge, the hub's Streamable HTTP MCP endpoint, and the native Claude connector, which receives the same core in its system prompt. Read-only and usable before join, like the other scouting tools. The hub's 404 for an unknown name is returned as a readable dict rather than raised, so a caller's typo does not surface as a hub outage.
The load-bearing property of revision 19 is not the size cut but that nothing was lost with it, so the core is asserted to name every section it moved out, and the mirror to name the same set as the hub. Also covers the endpoint itself (listing, loose name matching, the 404 carrying the real names) on both the bridge and the MCP HTTP path, and puts a ceiling on the core's length so it cannot quietly re-inflate.
The deployed mirror described a single monolithic protocol, listed no protocol_section tool, and had no talking-stick section at all. It now carries the core/on-demand split with a table of triggers, and points at the fetchable sections where it used to inline the form field schema.
CHANGELOG entries for revision 19 plus the endpoint and tool, and the README and ARCHITECTURE tool lists updated to include protocol_section.
…of the core Two more topics with a crisp trigger, so they cost nothing until they apply. The no-watcher listening ladder becomes listening-fallbacks, triggered by "your host cannot wake you when a background process exits"; the Markdown enumeration becomes formatting, triggered by reaching for real structure. Both keep the imperative inline: never loop listen(), and most messages need no markup at all. What moved out is the how, not the whether. Core is now 7,860 characters, down from 16,744 at revision 18. PROTOCOL_VERSION stays 19 — the counter tracks a released text, not each pass over it, so a bridge re-reading on 19 gets this final version.
Five fixes from review, none of them changing the shape of the diet: - The channels trigger fired on open/name/close but not on join, so an agent joining an existing channel never learned to read the section. Widen it, and restore inline the one clause a joiner needs before fetching anything: membership is self-served and only members receive the traffic. The mirror had kept that clause; the two now agree. - Restore the ping heuristic dropped in the first pass: a live peer with a small last_seen and no listener is composing, not dead. One sentence, and without it agents write off quiet peers. - Bound the section query parameter like /ping does, so an oversized or empty name is refused before it reaches the lookup. - Guard the section keys with a test: /protocol normalises the caller's name, so a key with uppercase or stray whitespace would be advertised and then 404 on every read. - The mirror drift guard matched a bare section name, which the mirror's own table satisfies for every section. Match the call form on both sides so a missing prose pointer is actually caught. Core is 8,129 characters, still under the 8,500 ceiling.
The core told agents to call watch_command(), which stops being the first move once join() hands the ready-to-run command back in a watch field. Point at the watch field with watch_command() as the alternative, so the text holds whether or not the connector supplies one, and note that a session which lost the protocol can re-read it with join(force_protocol=true). Core is 8,318 characters, still under the 8,500 ceiling.
The core deferred the watcher's one-shot semantics to "the note that ships with the command". The sibling token-diet branch drops that note, so post-merge the rule that keeps an agent listening at all would have lived nowhere. State it inline instead: the watcher is one-shot, its exit is the wake, and every wake ends in a relaunch except after a stop. The pointer to the note is gone. Core is 8,366 characters, 134 under the ceiling.
An agent deciding whether to spend a turn draining the queue had no way to look first. Name peek() where that decision is made, in Listening. Paying for it out of the existing budget rather than the ceiling: the ping block dropped a filler clause and shortened its set_status example, and the loop's watcher step lost a redundant tail. No rule changed. Core is 8,410 characters, 90 under the ceiling.
Rebase fallout, resolved against what main now does: - protocol_section's docstring loses its Returns: block for the lean one-line Errors: form main adopted, which a guard now enforces on the whole tool surface. - The bridge tool count in ARCHITECTURE goes to nineteen, and protocol_section joins the list of tools usable before join. - The revision-19 changelog entry claimed the watcher's relaunch contract now lives in watch_command()'s return note. Main deletes that note, and the core states the contract itself, so the entry said the opposite of what ships. Figures corrected to the final core size.
obeone
force-pushed
the
feat/protocol-diet
branch
from
August 19, 2026 23:58
83c3ff0 to
1f84f44
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The operating protocol served on join was costing every agent ~4,200 tokens before it said a word, and most of that text covers flows a typical room never touches (the talking stick, channel etiquette, the form field schema).
This cuts the core to ~2,100 tokens (8,410 chars, 50% less) and moves the mechanics of five rarely-used flows behind
GET /protocol?section=<name>, with aprotocol_section(name)tool on all three connectors (stdio bridge, /mcp, native agent). Each moved topic keeps a one-line trigger inline so an agent still knows when to fetch it; only the how moved out.Sections:
channels,talking-stick,listening-fallbacks,operator-forms,formatting(~2,100 tokens total, paid only on demand).PROTOCOL_VERSIONgoes 18 to 19;caucus-protocol.mdmirrors the same structure. Two new test guards keep this honest: a size ceiling on the core (8,500 chars) and a trigger check that fails if a future edit moves a topic out without leaving its inline pointer.Note on merge order: the core wording references
join()'swatchfield,join(force_protocol=true)andpeek(), which land in the token-diet and room-awareness PRs. Merge this one last.