Narrow-set filters the seat's own queue, so a receiver always has an identity (comms-g5zh.6) - #212
Merged
Merged
Conversation
…identity (comms-g5zh.6) The narrow set was a client-side filter over the minter's full event stream, and `matches` took `IdentityId | undefined` because pre-acquire there was no identity to compare a message's mentions against. With the queue registered against the seat's own principal, an unbound seat owns no queue to poll: `ownerHttp` refuses and the producer idles rather than polling. Nothing can reach `matches` without a binding, so the optional parameter described a state that no longer exists. `mentionsMatches` was the guard and nothing else — with the guard gone it forwards to `mentionsIdentity`, so it goes too. At the pump's call site an absent binding now means "not this seat's event" rather than "match on channel narrows alone": the same statement the producer makes one layer down. Claude-Session: https://claude.ai/code/session_016126ihUG75C88Zs5ckuvnp
GraemeF
added a commit
that referenced
this pull request
Jul 31, 2026
… nothing (comms-g5zh.7) (#213) The minter subscribed to every public stream so it could receive on behalf of a seat that had not yet minted, kept current by a boot-time reconciler. With the events queue (#209) and the channel subscriptions (#211, #212) both registered under the seat's own principal, no seat receives through another — and that layer has nothing left to do. The minter keeps the jobs that are its own: minting bots, and the directory and history reads that need no principal. **Removed** - `packages/zulip/minter-reconciler.ts` and its tests. - `reconcileMinterSubscriptions` from the Zulip adapter's shape, its wiring, and its response schema. - The boot-time reconcile call in the plugin's `main`, and its logging. - The no-op stub in the above-port test seam. - The adapter and `server.test.ts` tests covering the deleted capability. `lazy mode does NOT mint at boot when COMMY_SUBSCRIBE is unset` already asserts the property that matters — a seat that never mints subscribes to nothing. **Corrected** Prose that described the retired arrangement: `tools.ts` still claimed subscribe registered the *minter* for post-boot streams (it has gone out on the seat's own credential since #211), the adapter's per-event filter named the minter's subscription list, and the client README's boot diagram opened with the reconcile step. `docs/agent-experience.md` now reads as the worked example predicted: topic narrows and the event-queue handle are what remain client-side, both filtering the agent's own queue. Pure subtraction — no new capability, no shims. Subscription reconstruction (comms-70g4) is downstream of this, not part of it. https://claude.ai/code/session_01P67vRP5hs9Yvo16vkWru4g
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.
narrow-set.tswas a client-side filter over the minter's full event stream, somatchestookIdentityId | undefined: pre-acquire there was no identity to compare a message's mentions against, and the mention arm had to be gated on that.With the events queue registered against the seat's own principal (#209, #211), an unbound seat owns no queue to poll —
ownerHttprefuses and the producer idles instead of polling (packages/zulip/events.ts, theUnboundEphemeralSessioncatch). Nothing can reachmatcheswithout a binding, so the optional parameter described a state that no longer exists.What this removes:
botIdentityId !== undefinedguard, and with itmentionsMatches— once the guard goes it just forwards tomentionsIdentity, so the wrapper goes too;IdentityId | undefinedonNarrowSet.matches;a mention does not match pre-acquire) — it asserts a case that is no longer representable, and its "a mention of another identity doesn't match" content is already covered bya mention matches message-posted only when the bot is among the mentions.At the pump's call site (
server.ts) an absent binding now means "not this seat's event" rather than "match on channel narrows alone" — the same statement the producer makes one layer down. It is reachable only if a release lands between an event being produced and dispatched; there is no test for that arm, since the closure isn't reachable from outsidemakeProgram.The remaining client-side filtering is what the substrate genuinely cannot express: Zulip has no per-topic delivery primitive, so thread narrows stay here. Recording thread intent as realm state via
POST /user_topicswould demote this filter from authority to cache — real, but a tightening rather than a subtraction, so it's left as follow-up.The rest of the test churn is mechanical: 45
noBot(= undefined) call sites become a real receiving-seat id.Gate green inside
nix develop: 1212 pass, 0 fail.