Repository navigation
Conversation
Matrix permalinks change with each triggering event. Rendering one in the pinned session prompt made the prompt stale or changed its bytes every turn, which breaks conversation prompt caching. Put the link in the current model-facing user message and remove it from the durable transcript, including queued turns. Respect effective PII redaction when adding the note. Percent-encode room IDs, event IDs and the via server so delimiters cannot change the matrix.to URL structure. Preserve the Matrix and Discord source-note handling when integrating the upstream prefix removal change. Use removeprefix after selecting the platform-specific note, so each transport removes only its own generated prefix.
The live Matrix exchange now checks the model request for a permalink to the event sent by the independent client. It also verifies that the link is absent from the system prompt, which must remain stable across turns. Record the exchange duration separately from container setup time.
Room version 12 IDs have no server component. A permalink built only from the room ID therefore omitted the server hint, which can prevent a client outside the room from locating it. Use the joined bot account's server when the room ID has no domain. The builder still leaves via absent when neither server is known.
A room ID can identify a server that no longer participates in the room. Its server may also include a port or IPv6 address, which the last-colon parser truncated to an invalid via hint. Use the joined bot account's server for inbound source links. When only a room ID is available, parse its full server after the first colon. The same parse also corrects the room's session scope.
In a thread, every turn's source link pointed at the thread root. The root was chosen as a stable anchor while the link lived in the pinned session prompt, which is per thread. The link now goes on each user turn, so the reason no longer applies, and the model never received the ID of the message that triggered the turn. A request to cite "this message" produced a link to the root, which may be another user's message. Build the per-turn link from the triggering event in all cases. A matrix.to link to an event inside a thread opens that thread in current clients, and the session prompt already gives the thread root.
A room-ID permalink needs via servers so that a reader outside the room can join through one of them. The link carried only the bot's server. When that server was unreachable from the reader's server, the link failed. Choose up to three servers as the Matrix specification recommends for room-ID permalinks. The first is the server of the highest-power joined member at power level 50 or above; room version 12 creators count as the highest power. The rest are the servers with the most joined members, and IP literals are never chosen. The adapter reads members, power levels and the create event from the mautrix state store, which sync keeps current, so building a link makes no request. When the store has no joined members for the room, use the bot's server and then the room ID's server. Server ACLs are not applied, because the state store does not keep m.room.server_acl. Move the builder and the selection into a sibling module, and emit one via parameter per server.
The session prompt stopped rendering the Matrix source link when the link moved to the current user turn. Two prompt tests still asserted that the prompt omitted the link when it was unset or redacted. No code path produces that output any more, so neither test could fail because of this feature. The prompt-stability test covers the remaining contract, and the per-turn tests cover the note and its PII suppression. Restore the comment on the per-turn notes. strip_inbound_source_note removes a note only when it is the first thing in the message, so a later prefix added outside the notes would leave the note in the persisted transcript.
Choosing source-link via servers made every inbound message await get_power_levels and get_create on the client's state store. The project-context isolation and voice tests gave the adapter a MagicMock store with only get_members awaitable, so all six inbound tests in those files failed with "'MagicMock' object can't be awaited". Production always uses mautrix's MemoryStateStore. Give both fakes a real MemoryStateStore in which the bot and the sender have joined the test rooms. The real store implements every state query, so these tests keep working when the adapter starts reading other room state. The stores are filled with MemoryStateStore.deserialize because the helpers are synchronous and some are called inside a running event loop.
The effective-state parent added a description to the matrix_read limit argument: "Maximum number of events to read". In this layer the same argument also bounds the members kind, which returns members rather than events, and the pins kind. Say that the limit applies to events or members so the model can tell that it bounds member lists too.
The merged matrix_read description said that events are listed oldest first and that `errors` lists events that could not be decrypted. Both sentences came from the history reads. A pins read returns events in the order of the room's m.room.pinned_events list, and its errors also cover pins that could not be fetched, are not visible messages, came back as another event, or whose edit was redacted. A model that read the description could tell a user that a deleted pin was an encrypted message. Limit the ordering sentence to room and thread reads, say that pins follow the room's pinned order, and say that `errors` lists events that could not be read or decrypted, with the reason. History reads already returned non-decryption errors such as "event content changed", so the wider wording is also accurate for them.
afecc7e to
11056d8
Compare
Matrix compares the room with the snapshot saved on the newest user row that has one, under display_metadata["channel_state"]. Compaction replaced the summarised rows with a summary row that had no snapshot. When the summarised rows included every snapshot newer than the protected head, the baseline became an older snapshot in the head (the first turn's, with the default protect_first_n) or the session origin once the head no longer protects any user row. The next turn then repeated a name or topic note that the model had already seen, and it compared member and security state against stale or unknown values. Compaction removes the rows that contain the baseline, so compaction now keeps it. keep_newest_channel_state in agent/message_metadata.py records the newest snapshot of the original rows on the summary row (or on the tail row that absorbs the summary) when the compacted rows no longer have it as their newest. Tail rows come after the summary row, so a newer snapshot in the tail still wins. record_absorbed_message, which every fold of one row into another calls, applies the same rule, so the survivor of a merge of adjacent user rows (micro-compaction, alternation repair) keeps the later row's snapshot. The summary row can be an assistant row, so the gateway now reads the newest snapshot on a row of any role. CHANNEL_STATE_METADATA_KEY moves to agent/message_metadata.py because the compressor needs it, and acknowledged_channel_state is replaced by newest_channel_state.
When the compressor merges its summary into a tail row instead of adding a standalone row, that tail row must take the newest channel state from the rows it replaces. No test reached this path, so dropping the carrier assignment in _assemble_compressed passed the suite. Call _assemble_compressed directly for a summary merged into tail[0], for one merged into the first visible tail row when the head is empty, and for a tail that already has a newer snapshot, which must win.
Matrix changes lacked a live exchange test, so mocked adapter tests could miss failures between Synapse, gateway setup, and model-visible context. Start disposable Synapse and a checkout-built Linux gateway through Testcontainers. Drive events with a separate matrix-nio account and a fake model under an isolated HERMES_HOME. Provision a Linux nio client with its own encrypted device store so later encrypted cases can also run from macOS hosts. Run the suite in a dedicated CI job through scripts/run_tests.sh. The plain-room test confirms both the reply and the inbound model context.
Synapse changes the owner of /data to UID 991 when it generates its configuration. On a Linux runner, the host test process cannot append to the resulting homeserver.yaml. Store Synapse data in a Docker volume and edit the configuration from a one-shot container. The test runner can kill a timed-out file worker before pytest tears down its fixtures. Enable Testcontainers' Ryuk reaper, pin its image by digest, and start it before creating the volume. Label the Synapse volume with the session ID; Testcontainers labels its network and containers. Ryuk can then remove these resources when the worker exits.
Parallel Matrix live tests built different checkouts under the same hermes-matrix-live:checkout tag. A second build could replace the tag between the first build and its container start, so a green test might exercise the wrong code. Give each fixture invocation a UUID image tag and remove that exact tag after its containers stop. Concurrent branches can now run the live suite without changing which checkout an existing fixture starts.
A file timeout kills the pytest worker and its build process without running fixture teardown. The unique image tag from the live test can then remain on the Docker host indefinitely. Start Ryuk before building and label the checkout image with the same Testcontainers session ID as its containers, network and volume. Ryuk can remove the image after the worker dies. A SIGKILL probe confirmed that the pinned Ryuk image removes a labelled Docker image.
Matrix live tests built the production image for each checkout. That build prepares SQLite, Chromium, Node and the UI even though the tests start only a gateway. The separate Linux observer also built a second Python environment at test setup time to obtain matrix-nio crypto. Build a small Debian image with the PM-pinned Python and the locked Matrix and test dependencies before copying the checkout source. The gateway and observer use the same prepared interpreter in separate containers, while the gateway still runs against an isolated home and the host fake model. Keep gateway logs in timeout failures so startup problems remain diagnosable. The plain-room test passed with a 1.545-second test body and 26.8 seconds for the complete test file. On this host, an uncached image build took 128.1 seconds and a cached rebuild took 4.67 seconds.
The live plain-room test requested a one-second Synapse poll and allowed a minute before reporting a missing reply. Matrix-nio can retry a sync request beyond that loop deadline, so a stalled exchange could take even longer to fail. Request a 250 ms server wait and bound each sync by the remaining 15-second deadline. Successful replies still arrive through the real Matrix event stream. The separate-client Synapse test passed.
The approval client sends cards, seed reactions and requester controls in quick succession. Synapse's default account event limit exhausts its burst and rejects the controls before the short client deadline. Configure the disposable server for 100 events per second with a burst of 100. The limit remains finite, and tests can exercise protocol events without waiting for the production rate limiter to refill. Client waits, retry rules and assertions are unchanged.
The Matrix live fixtures skipped with "require a running Docker daemon" on macOS installations whose daemon is reachable only through a Docker CLI context, such as Docker Desktop or Colima without the /var/run/docker.sock symlink. The fixture replaced DOCKER_CONFIG with an empty configuration to keep registry credentials away from the tests. That also discarded currentContext and the context metadata, so the Docker SDK, Testcontainers and the docker CLI all fell back to /var/run/docker.sock. scripts/run_tests.sh removed DOCKER_HOST, DOCKER_CONTEXT and DOCKER_CONFIG from the test environment, so a user could not select the socket explicitly either. The fixture now resolves the endpoint the same way the docker CLI does (DOCKER_HOST, then DOCKER_CONTEXT, then currentContext) before it isolates the configuration. It exports the result as DOCKER_HOST and removes DOCKER_CONTEXT, so the SDK probe, Testcontainers and the buildx build all connect to that endpoint. The isolated configuration keeps the CLI plugin directories, because Docker Desktop and Homebrew install buildx outside the default search path. It still contains no registry credentials or credential helpers. On macOS the daemon runs in a Linux VM and resolves bind-mount sources there. Testcontainers mounts the client-side socket path into Ryuk by default, which fails when that path is a host socket such as a context's. The fixture therefore mounts /var/run/docker.sock, the socket inside the VM, into Ryuk on macOS. The runner forwards DOCKER_HOST, DOCKER_CONTEXT and DOCKER_CONFIG. It still drops DOCKER_AUTH_CONFIG, DOCKER_CERT_PATH and DOCKER_TLS_VERIFY. With a scratch context whose socket forwards to OrbStack and counts connections, the plain exchange passed with 70 API connections through that socket. The previous fixture passed the same invocation with no connections through it, because it used /var/run/docker.sock instead. A context pointing at a missing socket now skips with a message that includes the socket path.
The last dependency step of the test Dockerfile imports olm, mautrix.crypto and nio, so an image without Matrix crypto support already fails to build, and the fixture reports the build log. The fixture then started a container from the finished image only to run the same imports again. That check could not fail, and it added a container start to every test module.
The original feature tests predate the canonical approval lifecycle, permalink source fields, room policy owner argument and SDK send error classification. Their doubles omitted asynchronous state-store reads, exact approval request IDs and terminal card edits. Several complete expected events also omitted the newly composed source and mention metadata, so the full feature gate failed before checking its intended behaviour. Update those fixture bindings and complete expected values for the current defining owners. Keep every original parameter row, delivery attempt, payload, cancellation and authorization assertion. Thread reconnect expectations retain confirmed delivery and cache identity. Catch-up expectations include the permalink note before the new message. The complete nine-file feature boundary passes all 808 cases with the actual installed Matrix SDK, four workers and no file retries. The full Matrix file finishes in 30.7 seconds, including the event-loop lookup contract that timed out on the earlier incomplete source composition. Original858 public source qualification The preceding 808-case result records the original composition. It does not describe a new run against this public tree. The published backfill composition has 343 scoped cases, using 264 final affected cases and 79 unchanged earlier successes. Its inherited formatter and type findings remain qualified, and live Docker validation remains pending. The other feature contracts are represented by their existing public owners. Their fixture expectations follow the actual approval, notice, location, send-classification, read-policy and delivered-event APIs. Expectations from the original later composition do not establish bugs in standalone owners that use an earlier API. No combined six-module endpoint or repeated native gate is required for this source credit. This source-neutral contribution preserves the complete original message and both original contributor dates. It records the distributed source qualification without changing the published source tree. Current linear successor qualification The 343-case result above describes the earlier a76019d composition. This contribution follows the verified single-parent successor a83881c and keeps its source tree unchanged. The accepted seven-file gateway gate for that successor passes 362 cases. Live Matrix validation, private integration and whole-campaign completion remain separate.
The Matrix SDK retries rate-limit responses and connection timeouts indefinitely by default. A failed request during account or room setup can therefore stop the whole native module until the outer runner kills it, without reporting the setup error. Disable those retries and bound ordinary fixture requests to fifteen seconds. Apply the same configuration to registration, restored accounts and the encrypted observer. The fixtures now expose the SDK error so the failing operation can be diagnosed directly. A real loopback HTTP server reproduces the repeated rate-limit requests through registration and restored accounts. Both paths now make one request and return the expected error.
Matrix turns could read message history, but the agent could not inspect the current room state, joined members, power levels or pinned messages. Those details are available from the receiving bot and can affect what the agent should say or attempt. Extend the Matrix-only read tool with bounded inspection kinds. Fetch state and members through the owning adapter, apply its existing room and requester policy, and resolve pinned messages with the same decryption path as event reads. Report missing pin content per event so the other pins remain visible. Include the event type and creator override when presenting permission thresholds. A separate Matrix client verifies that the model receives the room details and pinned message from a disposable Synapse instance. Use the effective-state parent's client-events decoder and gateway scheduling path throughout inspection and its tests. Recheck admission after identity resolution, derive permission defaults from raw state, and preserve cancellation while refreshing pinned content. The feature and these interface migrations form one change so the initial commit uses the current prerequisite APIs.
The dependency-rebinding fixtures installed a fake importer but left the package-availability check unchanged. With a complete SDK installed, ensure_matrix_deps skips the importer, so these fixtures failed without exercising their intended success and refusal paths. Report a missing Matrix package in the fixture alongside the existing installation seam. The real rebind and failed-import assertions then exercise the intended fresh-install boundary.
The Matrix adapter ignores emotes and does not register native sticker events. Emotes describe the sender's action, while stickers use a separate event type with a description and an image. The existing text handler would interpret a slash-like action body as a gateway command, so native actions need attributed text before command classification. Accept both through the existing room, sender and mention gates. Prefix emotes with their actor, preserve sticker descriptions, and download sticker pixels through the bounded media path. Use the same labels in effective reads, replies and thread history. Queued stickers can be edited or redacted while media preparation is pending. Add an optional media-event snapshot capability and retain the source paths of image descriptions, so final model preparation removes both withdrawn pixels and their derived text. Existing platform snapshots remain compatible and previous conversation context remains unchanged. The two parameterised invariants reproduce six failures on the committed parents and now cover native SDK events, gates, unavailable media and withdrawal during asynchronous preparation. Matrix and adjacent gateway checks pass against the pinned SDK source.
e613dbb to
471fa57
Compare
Debounced text could bypass the runner's merge checks and inherit the sender or control permissions of a pending photo. Photo coalescing also moved later captions ahead of earlier queued messages and discarded a conflicting reply target. Use one predicate for sender, control and plugin metadata, and reply compatibility. Submit debounced bursts through bounded FIFO admission, and coalesce a photo burst only while the overflow is empty. Standalone adapters keep incompatible bursts in their existing debounce state so a third sender or reply remains independent. The normal drain, discard and shutdown paths process or preserve those events; cap refusal leaves the previously accepted events intact. An attempted FIFO head could be deferred again while the next event was already staged. Appending the attempted event rotated the queue. Track the adapter's dispatch within its current task and restore that event to the front when the handler queues it again. Source and message identity also recognise rewrite copies, with timestamps for events without IDs. The existing retry backoff still applies, while unrelated arrivals keep their place at the end of the queue. Independent probes reproduced permission inheritance, photo overtaking, reply loss and FIFO rotation on the prior head. The repaired contracts and adjacent tests pass, including rewrite copies, reconnect backoff and standalone drain, discard, shutdown and cap refusal. The preceding extraction moved the debounce state and methods into gateway/platforms/base_text_debounce.py. Apply these declaration and method changes there, retaining typed receivers and explicit field imports. This source replaces the earlier shared sender functions with its new base_pending.py declarations. Remove their old defining-sibling copies and update consumers to the new defining imports. Keep the upstream selected-owner loop and its staging call; apply only the original acceptance assignment in that handler. The runner uses its existing defining-module merge-helper import. Maintenance adaptation: remove the surplus terminal blank line from the extracted debounce module. All implementation syntax remains unchanged. Parent alignment: retain the inherited same-sender a/b/a arrival case from NousResearch#129160. FIFO preserves all three reply contexts, so the expected turns remain separate a, b and a events. Its input, case ID and whole event assertion remain unchanged.
kvnloo
left a comment
There was a problem hiding this comment.
Scoped review of the documented own layer (5724ccfd..471fa579, 16 commits): no blocking finding in the reply-context/batching changes. Shared adapters now split on conflicting explicit reply contexts and preserve compatible/plain+reply context, contributor message ids, attachment order and inline flags; the tests exercise those cases across Discord/Matrix/WhatsApp/SimpleX/WeCom. Telegram’s separate boundary buffer keeps the earlier batch tracked through cancellation/reconnect instead of losing it after the quiet-period task is replaced. The busy debounce regressions also pin the three-message order/reply-context cases. This is not a clean bill for the whole 187-file stacked view: #126281 remains a prerequisite, and the known cross-sender merge/cap work stays separately scoped in dependent #129126/#130445.
471fa57 to
4302fbe
Compare
Debounced text could bypass the runner's merge checks and inherit the sender or control permissions of a pending photo. Photo coalescing also moved later captions ahead of earlier queued messages and discarded a conflicting reply target. Use one predicate for sender, control and plugin metadata, and reply compatibility. Submit debounced bursts through bounded FIFO admission, and coalesce a photo burst only while the overflow is empty. Standalone adapters keep incompatible bursts in their existing debounce state so a third sender or reply remains independent. The normal drain, discard and shutdown paths process or preserve those events; cap refusal leaves the previously accepted events intact. An attempted FIFO head could be deferred again while the next event was already staged. Appending the attempted event rotated the queue. Track the adapter's dispatch within its current task and restore that event to the front when the handler queues it again. Source and message identity also recognise rewrite copies, with timestamps for events without IDs. The existing retry backoff still applies, while unrelated arrivals keep their place at the end of the queue. Independent probes reproduced permission inheritance, photo overtaking, reply loss and FIFO rotation on the prior head. The repaired contracts and adjacent tests pass, including rewrite copies, reconnect backoff and standalone drain, discard, shutdown and cap refusal. The preceding extraction moved the debounce state and methods into gateway/platforms/base_text_debounce.py. Apply these declaration and method changes there, retaining typed receivers and explicit field imports. This source replaces the earlier shared sender functions with its new base_pending.py declarations. Remove their old defining-sibling copies and update consumers to the new defining imports. Keep the upstream selected-owner loop and its staging call; apply only the original acceptance assignment in that handler. The runner uses its existing defining-module merge-helper import. Maintenance adaptation: remove the surplus terminal blank line from the extracted debounce module. All implementation syntax remains unchanged. Parent alignment: retain the inherited same-sender a/b/a arrival case from NousResearch#129160. FIFO preserves all three reply contexts, so the expected turns remain separate a, b and a events. Its input, case ID and whole event assertion remain unchanged.
4302fbe to
276b0f7
Compare
The fake HTTP API discarded the client_session supplied by the Matrix adapter, so disconnect closed a mock while the real aiohttp session was left open. A reconnect test also replaced disconnect with a stub and left its new connection open after checking the guard. Strict resource warnings expose both leaks. Preserve the supplied session in the fake API. Use a wrapping disconnect spy in the reconnect test and disconnect the replacement client after the existing call assertion. The fake API and reconnect fixture predate this branch, so this focused fixture maintenance is separate from the corrections folded into their campaign contributors. Production code and all original assertions are unchanged.
Pending-event merges and busy-text debounce share reply and attachment semantics inside the large base adapter. Telegram's ingress overrides also modify the same batching boundary. Extract those existing topics before integrating the reviewed reply-context fixes. Repoint in-tree imports and patch targets to the defining merge module. The adapter classes inherit their existing methods, so timers, tracked redispatch and delayed-delivery ownership keep their current behaviour. The fixed upstream annotation cleanup overlaps the extracted debounce state. Keep the state in its existing topical module and apply the same unquoted task annotation there. The complete state and timer behaviour remain unchanged.
Debounced text could bypass the runner's merge checks and inherit the sender or control permissions of a pending photo. Photo coalescing also moved later captions ahead of earlier queued messages and discarded a conflicting reply target. Use one predicate for sender, control and plugin metadata, and reply compatibility. Submit debounced bursts through bounded FIFO admission, and coalesce a photo burst only while the overflow is empty. Standalone adapters keep incompatible bursts in their existing debounce state so a third sender or reply remains independent. The normal drain, discard and shutdown paths process or preserve those events; cap refusal leaves the previously accepted events intact. An attempted FIFO head could be deferred again while the next event was already staged. Appending the attempted event rotated the queue. Track the adapter's dispatch within its current task and restore that event to the front when the handler queues it again. Source and message identity also recognise rewrite copies, with timestamps for events without IDs. The existing retry backoff still applies, while unrelated arrivals keep their place at the end of the queue. Independent probes reproduced permission inheritance, photo overtaking, reply loss and FIFO rotation on the prior head. The repaired contracts and adjacent tests pass, including rewrite copies, reconnect backoff and standalone drain, discard, shutdown and cap refusal. The preceding extraction moved the debounce state and methods into gateway/platforms/base_text_debounce.py. Apply these declaration and method changes there, retaining typed receivers and explicit field imports. This source replaces the earlier shared sender functions with its new base_pending.py declarations. Remove their old defining-sibling copies and update consumers to the new defining imports. Keep the upstream selected-owner loop and its staging call; apply only the original acceptance assignment in that handler. The runner uses its existing defining-module merge-helper import. Maintenance adaptation: remove the surplus terminal blank line from the extracted debounce module. All implementation syntax remains unchanged. Parent alignment: retain the inherited same-sender a/b/a arrival case from NousResearch#129160. FIFO preserves all three reply contexts, so the expected turns remain separate a, b and a events. Its input, case ID and whole event assertion remain unchanged.
276b0f7 to
1defc7b
Compare
Debounced text could bypass the runner's merge checks and inherit the sender or control permissions of a pending photo. Photo coalescing also moved later captions ahead of earlier queued messages and discarded a conflicting reply target. Use one predicate for sender, control and plugin metadata, and reply compatibility. Submit debounced bursts through bounded FIFO admission, and coalesce a photo burst only while the overflow is empty. Standalone adapters keep incompatible bursts in their existing debounce state so a third sender or reply remains independent. The normal drain, discard and shutdown paths process or preserve those events; cap refusal leaves the previously accepted events intact. An attempted FIFO head could be deferred again while the next event was already staged. Appending the attempted event rotated the queue. Track the adapter's dispatch within its current task and restore that event to the front when the handler queues it again. Source and message identity also recognise rewrite copies, with timestamps for events without IDs. The existing retry backoff still applies, while unrelated arrivals keep their place at the end of the queue. Independent probes reproduced permission inheritance, photo overtaking, reply loss and FIFO rotation on the prior head. The repaired contracts and adjacent tests pass, including rewrite copies, reconnect backoff and standalone drain, discard, shutdown and cap refusal. The preceding extraction moved the debounce state and methods into gateway/platforms/base_text_debounce.py. Apply these declaration and method changes there, retaining typed receivers and explicit field imports. This source replaces the earlier shared sender functions with its new base_pending.py declarations. Remove their old defining-sibling copies and update consumers to the new defining imports. Keep the upstream selected-owner loop and its staging call; apply only the original acceptance assignment in that handler. The runner uses its existing defining-module merge-helper import. Maintenance adaptation: remove the surplus terminal blank line from the extracted debounce module. All implementation syntax remains unchanged. Parent alignment: retain the inherited same-sender a/b/a arrival case from NousResearch#129160. FIFO preserves all three reply contexts, so the expected turns remain separate a, b and a events. Its input, case ID and whole event assertion remain unchanged.
1defc7b to
cbd73a4
Compare
Debounced text could bypass the runner's merge checks and inherit the sender or control permissions of a pending photo. Photo coalescing also moved later captions ahead of earlier queued messages and discarded a conflicting reply target. Use one predicate for sender, control and plugin metadata, and reply compatibility. Submit debounced bursts through bounded FIFO admission, and coalesce a photo burst only while the overflow is empty. Standalone adapters keep incompatible bursts in their existing debounce state so a third sender or reply remains independent. The normal drain, discard and shutdown paths process or preserve those events; cap refusal leaves the previously accepted events intact. An attempted FIFO head could be deferred again while the next event was already staged. Appending the attempted event rotated the queue. Track the adapter's dispatch within its current task and restore that event to the front when the handler queues it again. Source and message identity also recognise rewrite copies, with timestamps for events without IDs. The existing retry backoff still applies, while unrelated arrivals keep their place at the end of the queue. Independent probes reproduced permission inheritance, photo overtaking, reply loss and FIFO rotation on the prior head. The repaired contracts and adjacent tests pass, including rewrite copies, reconnect backoff and standalone drain, discard, shutdown and cap refusal. The preceding extraction moved the debounce state and methods into gateway/platforms/base_text_debounce.py. Apply these declaration and method changes there, retaining typed receivers and explicit field imports. This source replaces the earlier shared sender functions with its new base_pending.py declarations. Remove their old defining-sibling copies and update consumers to the new defining imports. Keep the upstream selected-owner loop and its staging call; apply only the original acceptance assignment in that handler. The runner uses its existing defining-module merge-helper import. Maintenance adaptation: remove the surplus terminal blank line from the extracted debounce module. All implementation syntax remains unchanged. Parent alignment: retain the inherited same-sender a/b/a arrival case from NousResearch#129160. FIFO preserves all three reply contexts, so the expected turns remain separate a, b and a events. Its input, case ID and whole event assertion remain unchanged.
Debounced text could bypass the runner's merge checks and inherit the sender or control permissions of a pending photo. Photo coalescing also moved later captions ahead of earlier queued messages and discarded a conflicting reply target. Use one predicate for sender, control and plugin metadata, and reply compatibility. Submit debounced bursts through bounded FIFO admission, and coalesce a photo burst only while the overflow is empty. Standalone adapters keep incompatible bursts in their existing debounce state so a third sender or reply remains independent. The normal drain, discard and shutdown paths process or preserve those events; cap refusal leaves the previously accepted events intact. An attempted FIFO head could be deferred again while the next event was already staged. Appending the attempted event rotated the queue. Track the adapter's dispatch within its current task and restore that event to the front when the handler queues it again. Source and message identity also recognise rewrite copies, with timestamps for events without IDs. The existing retry backoff still applies, while unrelated arrivals keep their place at the end of the queue. Independent probes reproduced permission inheritance, photo overtaking, reply loss and FIFO rotation on the prior head. The repaired contracts and adjacent tests pass, including rewrite copies, reconnect backoff and standalone drain, discard, shutdown and cap refusal. The preceding extraction moved the debounce state and methods into gateway/platforms/base_text_debounce.py. Apply these declaration and method changes there, retaining typed receivers and explicit field imports. This source replaces the earlier shared sender functions with its new base_pending.py declarations. Remove their old defining-sibling copies and update consumers to the new defining imports. Keep the upstream selected-owner loop and its staging call; apply only the original acceptance assignment in that handler. The runner uses its existing defining-module merge-helper import. Maintenance adaptation: remove the surplus terminal blank line from the extracted debounce module. All implementation syntax remains unchanged. Parent alignment: retain the inherited same-sender a/b/a arrival case from NousResearch#129160. FIFO preserves all three reply contexts, so the expected turns remain separate a, b and a events. Its input, case ID and whole event assertion remain unchanged.
Debounced text could bypass the runner's merge checks and inherit the sender or control permissions of a pending photo. Photo coalescing also moved later captions ahead of earlier queued messages and discarded a conflicting reply target. Use one predicate for sender, control and plugin metadata, and reply compatibility. Submit debounced bursts through bounded FIFO admission, and coalesce a photo burst only while the overflow is empty. Standalone adapters keep incompatible bursts in their existing debounce state so a third sender or reply remains independent. The normal drain, discard and shutdown paths process or preserve those events; cap refusal leaves the previously accepted events intact. An attempted FIFO head could be deferred again while the next event was already staged. Appending the attempted event rotated the queue. Track the adapter's dispatch within its current task and restore that event to the front when the handler queues it again. Source and message identity also recognise rewrite copies, with timestamps for events without IDs. The existing retry backoff still applies, while unrelated arrivals keep their place at the end of the queue. Independent probes reproduced permission inheritance, photo overtaking, reply loss and FIFO rotation on the prior head. The repaired contracts and adjacent tests pass, including rewrite copies, reconnect backoff and standalone drain, discard, shutdown and cap refusal. The preceding extraction moved the debounce state and methods into gateway/platforms/base_text_debounce.py. Apply these declaration and method changes there, retaining typed receivers and explicit field imports. This source replaces the earlier shared sender functions with its new base_pending.py declarations. Remove their old defining-sibling copies and update consumers to the new defining imports. Keep the upstream selected-owner loop and its staging call; apply only the original acceptance assignment in that handler. The runner uses its existing defining-module merge-helper import. Maintenance adaptation: remove the surplus terminal blank line from the extracted debounce module. All implementation syntax remains unchanged. Parent alignment: retain the inherited same-sender a/b/a arrival case from NousResearch#129160. FIFO preserves all three reply contexts, so the expected turns remain separate a, b and a events. Its input, case ID and whole event assertion remain unchanged.
Debounced text could bypass the runner's merge checks and inherit the sender or control permissions of a pending photo. Photo coalescing also moved later captions ahead of earlier queued messages and discarded a conflicting reply target. Use one predicate for sender, control and plugin metadata, and reply compatibility. Submit debounced bursts through bounded FIFO admission, and coalesce a photo burst only while the overflow is empty. Standalone adapters keep incompatible bursts in their existing debounce state so a third sender or reply remains independent. The normal drain, discard and shutdown paths process or preserve those events; cap refusal leaves the previously accepted events intact. An attempted FIFO head could be deferred again while the next event was already staged. Appending the attempted event rotated the queue. Track the adapter's dispatch within its current task and restore that event to the front when the handler queues it again. Source and message identity also recognise rewrite copies, with timestamps for events without IDs. The existing retry backoff still applies, while unrelated arrivals keep their place at the end of the queue. Independent probes reproduced permission inheritance, photo overtaking, reply loss and FIFO rotation on the prior head. The repaired contracts and adjacent tests pass, including rewrite copies, reconnect backoff and standalone drain, discard, shutdown and cap refusal. The preceding extraction moved the debounce state and methods into gateway/platforms/base_text_debounce.py. Apply these declaration and method changes there, retaining typed receivers and explicit field imports. This source replaces the earlier shared sender functions with its new base_pending.py declarations. Remove their old defining-sibling copies and update consumers to the new defining imports. Keep the upstream selected-owner loop and its staging call; apply only the original acceptance assignment in that handler. The runner uses its existing defining-module merge-helper import. Maintenance adaptation: remove the surplus terminal blank line from the extracted debounce module. All implementation syntax remains unchanged. Parent alignment: retain the inherited same-sender a/b/a arrival case from NousResearch#129160. FIFO preserves all three reply contexts, so the expected turns remain separate a, b and a events. Its input, case ID and whole event assertion remain unchanged.
Debounced text could bypass the runner's merge checks and inherit the sender or control permissions of a pending photo. Photo coalescing also moved later captions ahead of earlier queued messages and discarded a conflicting reply target. Use one predicate for sender, control and plugin metadata, and reply compatibility. Submit debounced bursts through bounded FIFO admission, and coalesce a photo burst only while the overflow is empty. Standalone adapters keep incompatible bursts in their existing debounce state so a third sender or reply remains independent. The normal drain, discard and shutdown paths process or preserve those events; cap refusal leaves the previously accepted events intact. An attempted FIFO head could be deferred again while the next event was already staged. Appending the attempted event rotated the queue. Track the adapter's dispatch within its current task and restore that event to the front when the handler queues it again. Source and message identity also recognise rewrite copies, with timestamps for events without IDs. The existing retry backoff still applies, while unrelated arrivals keep their place at the end of the queue. Independent probes reproduced permission inheritance, photo overtaking, reply loss and FIFO rotation on the prior head. The repaired contracts and adjacent tests pass, including rewrite copies, reconnect backoff and standalone drain, discard, shutdown and cap refusal. The preceding extraction moved the debounce state and methods into gateway/platforms/base_text_debounce.py. Apply these declaration and method changes there, retaining typed receivers and explicit field imports. This source replaces the earlier shared sender functions with its new base_pending.py declarations. Remove their old defining-sibling copies and update consumers to the new defining imports. Keep the upstream selected-owner loop and its staging call; apply only the original acceptance assignment in that handler. The runner uses its existing defining-module merge-helper import. Maintenance adaptation: remove the surplus terminal blank line from the extracted debounce module. All implementation syntax remains unchanged. Parent alignment: retain the inherited same-sender a/b/a arrival case from NousResearch#129160. FIFO preserves all three reply contexts, so the expected turns remain separate a, b and a events. Its input, case ID and whole event assertion remain unchanged.
4f5f1cf to
627fb22
Compare
Debounced text could bypass the runner's merge checks and inherit the sender or control permissions of a pending photo. Photo coalescing also moved later captions ahead of earlier queued messages and discarded a conflicting reply target. Use one predicate for sender, control and plugin metadata, and reply compatibility. Submit debounced bursts through bounded FIFO admission, and coalesce a photo burst only while the overflow is empty. Standalone adapters keep incompatible bursts in their existing debounce state so a third sender or reply remains independent. The normal drain, discard and shutdown paths process or preserve those events; cap refusal leaves the previously accepted events intact. An attempted FIFO head could be deferred again while the next event was already staged. Appending the attempted event rotated the queue. Track the adapter's dispatch within its current task and restore that event to the front when the handler queues it again. Source and message identity also recognise rewrite copies, with timestamps for events without IDs. The existing retry backoff still applies, while unrelated arrivals keep their place at the end of the queue. Independent probes reproduced permission inheritance, photo overtaking, reply loss and FIFO rotation on the prior head. The repaired contracts and adjacent tests pass, including rewrite copies, reconnect backoff and standalone drain, discard, shutdown and cap refusal. The preceding extraction moved the debounce state and methods into gateway/platforms/base_text_debounce.py. Apply these declaration and method changes there, retaining typed receivers and explicit field imports. This source replaces the earlier shared sender functions with its new base_pending.py declarations. Remove their old defining-sibling copies and update consumers to the new defining imports. Keep the upstream selected-owner loop and its staging call; apply only the original acceptance assignment in that handler. The runner uses its existing defining-module merge-helper import. Maintenance adaptation: remove the surplus terminal blank line from the extracted debounce module. All implementation syntax remains unchanged. Parent alignment: retain the inherited same-sender a/b/a arrival case from NousResearch#129160. FIFO preserves all three reply contexts, so the expected turns remain separate a, b and a events. Its input, case ID and whole event assertion remain unchanged.
Debounced text could bypass the runner's merge checks and inherit the sender or control permissions of a pending photo. Photo coalescing also moved later captions ahead of earlier queued messages and discarded a conflicting reply target. Use one predicate for sender, control and plugin metadata, and reply compatibility. Submit debounced bursts through bounded FIFO admission, and coalesce a photo burst only while the overflow is empty. Standalone adapters keep incompatible bursts in their existing debounce state so a third sender or reply remains independent. The normal drain, discard and shutdown paths process or preserve those events; cap refusal leaves the previously accepted events intact. An attempted FIFO head could be deferred again while the next event was already staged. Appending the attempted event rotated the queue. Track the adapter's dispatch within its current task and restore that event to the front when the handler queues it again. Source and message identity also recognise rewrite copies, with timestamps for events without IDs. The existing retry backoff still applies, while unrelated arrivals keep their place at the end of the queue. Independent probes reproduced permission inheritance, photo overtaking, reply loss and FIFO rotation on the prior head. The repaired contracts and adjacent tests pass, including rewrite copies, reconnect backoff and standalone drain, discard, shutdown and cap refusal. The preceding extraction moved the debounce state and methods into gateway/platforms/base_text_debounce.py. Apply these declaration and method changes there, retaining typed receivers and explicit field imports. This source replaces the earlier shared sender functions with its new base_pending.py declarations. Remove their old defining-sibling copies and update consumers to the new defining imports. Keep the upstream selected-owner loop and its staging call; apply only the original acceptance assignment in that handler. The runner uses its existing defining-module merge-helper import. Maintenance adaptation: remove the surplus terminal blank line from the extracted debounce module. All implementation syntax remains unchanged. Parent alignment: retain the inherited same-sender a/b/a arrival case from NousResearch#129160. FIFO preserves all three reply contexts, so the expected turns remain separate a, b and a events. Its input, case ID and whole event assertion remain unchanged.
Debounced text could bypass the runner's merge checks and inherit the sender or control permissions of a pending photo. Photo coalescing also moved later captions ahead of earlier queued messages and discarded a conflicting reply target. Use one predicate for sender, control and plugin metadata, and reply compatibility. Submit debounced bursts through bounded FIFO admission, and coalesce a photo burst only while the overflow is empty. Standalone adapters keep incompatible bursts in their existing debounce state so a third sender or reply remains independent. The normal drain, discard and shutdown paths process or preserve those events; cap refusal leaves the previously accepted events intact. An attempted FIFO head could be deferred again while the next event was already staged. Appending the attempted event rotated the queue. Track the adapter's dispatch within its current task and restore that event to the front when the handler queues it again. Source and message identity also recognise rewrite copies, with timestamps for events without IDs. The existing retry backoff still applies, while unrelated arrivals keep their place at the end of the queue. Independent probes reproduced permission inheritance, photo overtaking, reply loss and FIFO rotation on the prior head. The repaired contracts and adjacent tests pass, including rewrite copies, reconnect backoff and standalone drain, discard, shutdown and cap refusal. The preceding extraction moved the debounce state and methods into gateway/platforms/base_text_debounce.py. Apply these declaration and method changes there, retaining typed receivers and explicit field imports. This source replaces the earlier shared sender functions with its new base_pending.py declarations. Remove their old defining-sibling copies and update consumers to the new defining imports. Keep the upstream selected-owner loop and its staging call; apply only the original acceptance assignment in that handler. The runner uses its existing defining-module merge-helper import. Maintenance adaptation: remove the surplus terminal blank line from the extracted debounce module. All implementation syntax remains unchanged. Parent alignment: retain the inherited same-sender a/b/a arrival case from NousResearch#129160. FIFO preserves all three reply contexts, so the expected turns remain separate a, b and a events. Its input, case ID and whole event assertion remain unchanged.
Debounced text could bypass the runner's merge checks and inherit the sender or control permissions of a pending photo. Photo coalescing also moved later captions ahead of earlier queued messages and discarded a conflicting reply target. Use one predicate for sender, control and plugin metadata, and reply compatibility. Submit debounced bursts through bounded FIFO admission, and coalesce a photo burst only while the overflow is empty. Standalone adapters keep incompatible bursts in their existing debounce state so a third sender or reply remains independent. The normal drain, discard and shutdown paths process or preserve those events; cap refusal leaves the previously accepted events intact. An attempted FIFO head could be deferred again while the next event was already staged. Appending the attempted event rotated the queue. Track the adapter's dispatch within its current task and restore that event to the front when the handler queues it again. Source and message identity also recognise rewrite copies, with timestamps for events without IDs. The existing retry backoff still applies, while unrelated arrivals keep their place at the end of the queue. Independent probes reproduced permission inheritance, photo overtaking, reply loss and FIFO rotation on the prior head. The repaired contracts and adjacent tests pass, including rewrite copies, reconnect backoff and standalone drain, discard, shutdown and cap refusal. The preceding extraction moved the debounce state and methods into gateway/platforms/base_text_debounce.py. Apply these declaration and method changes there, retaining typed receivers and explicit field imports. This source replaces the earlier shared sender functions with its new base_pending.py declarations. Remove their old defining-sibling copies and update consumers to the new defining imports. Keep the upstream selected-owner loop and its staging call; apply only the original acceptance assignment in that handler. The runner uses its existing defining-module merge-helper import. Maintenance adaptation: remove the surplus terminal blank line from the extracted debounce module. All implementation syntax remains unchanged. Parent alignment: retain the inherited same-sender a/b/a arrival case from NousResearch#129160. FIFO preserves all three reply contexts, so the expected turns remain separate a, b and a events. Its input, case ID and whole event assertion remain unchanged.
Debounced text could bypass the runner's merge checks and inherit the sender or control permissions of a pending photo. Photo coalescing also moved later captions ahead of earlier queued messages and discarded a conflicting reply target. Use one predicate for sender, control and plugin metadata, and reply compatibility. Submit debounced bursts through bounded FIFO admission, and coalesce a photo burst only while the overflow is empty. Standalone adapters keep incompatible bursts in their existing debounce state so a third sender or reply remains independent. The normal drain, discard and shutdown paths process or preserve those events; cap refusal leaves the previously accepted events intact. An attempted FIFO head could be deferred again while the next event was already staged. Appending the attempted event rotated the queue. Track the adapter's dispatch within its current task and restore that event to the front when the handler queues it again. Source and message identity also recognise rewrite copies, with timestamps for events without IDs. The existing retry backoff still applies, while unrelated arrivals keep their place at the end of the queue. Independent probes reproduced permission inheritance, photo overtaking, reply loss and FIFO rotation on the prior head. The repaired contracts and adjacent tests pass, including rewrite copies, reconnect backoff and standalone drain, discard, shutdown and cap refusal. The preceding extraction moved the debounce state and methods into gateway/platforms/base_text_debounce.py. Apply these declaration and method changes there, retaining typed receivers and explicit field imports. This source replaces the earlier shared sender functions with its new base_pending.py declarations. Remove their old defining-sibling copies and update consumers to the new defining imports. Keep the upstream selected-owner loop and its staging call; apply only the original acceptance assignment in that handler. The runner uses its existing defining-module merge-helper import. Maintenance adaptation: remove the surplus terminal blank line from the extracted debounce module. All implementation syntax remains unchanged. Parent alignment: retain the inherited same-sender a/b/a arrival case from NousResearch#129160. FIFO preserves all three reply contexts, so the expected turns remain separate a, b and a events. Its input, case ID and whole event assertion remain unchanged.
Stack
Nesting records logical merge order. #129160 follows both #126281 and #135482. The worker-loop repair in #135482 is independent and can merge at any time; its commits are not duplicated in this branch.
Change
Busy follow-ups preserve reply targets and quoted content when chunks combine. Compatible reply contexts merge; conflicting explicit contexts create separate batches. Shared batching retains arrival order, the latest inbound message identity, all contributor IDs, attachment captions, aligned inline-media flags and context dependencies. This covers normalised Discord, Matrix, WhatsApp, SimpleX and WeCom events.
Telegram resolves each batching key once and dispatches reply-boundary batches through tracked tasks. Cancellation and reconnect teardown preserve both the earlier boundary event and the pending event. Shared batching and delayed Telegram delivery are extracted into topical modules.
The six adopted follow-ups from #132243 retain their complete original contribution metadata. Integration preserves the upstream media and context absorption helpers in the extracting commits.
Current composition and validation
I resolved the concrete extraction conflicts against the fixed upstream revision
3637c512fc3211bdccb61b7140d09ac1018508baand reapplied this layer onto the repaired #126281 supplier. The debounce state and batching methods remain in their existing topical modules. The same upstream unquoted annotations are applied inside those modules. Three bounded joins occur in contributions one, seven and fourteen; every other complete join is automatic. All sixteen complete author and committer headers, timestamps, timezones, original message prefixes, order and the empty thirteenth record are preserved. The supplier's checkpoint, admission, room, real HTTP-session and store teardown repairs remain intact.All 172 assertions pass across the nine complete own-layer files. Seven children, with 146 assertions, have clean resource shutdown. The other two complete children pass 26 assertions but emit twelve database shutdown warnings. Both complete test modules are byte-identical to the preceding published #129160 source, whose complete child outputs have the same warning counts. These warnings are reported separately; passing assertions do not establish clean shutdown for those two files.
The Matrix and reaction gate uses the unchanged three-record #135482 prerequisite at its captured public revision
a4027e0661448650ff40cdd7cda0269c3127a9fc. All 311 assertions pass across both complete children, with no unclosed database, event loop, socket or transport. Those three records are a logical prerequisite and are not incorporated into this feature branch.Both gates use the canonical runner, the installed Matrix SDK, four workers, zero retries and four strict warning categories. Full child output through interpreter shutdown is retained. All 8,470 pure-branch inputs and 8,472 composed inputs are authenticated against their exact runtime Git objects before and after execution. The runner is restored exactly. The final source differs from the tested source by formatting one complete batching module and removing one surplus EOF newline from an unused supplier test module. Both complete module ASTs are identical, and all selected runtime and type-check source inputs remain unchanged apart from the already checked batching formatting. These qualified scopes account for 483 passing assertions across eleven complete files, with 457 assertions in nine children that have clean resource shutdown.
The twenty-two-path type comparison has 632 findings. Of these, 628 complete lexical and defining-statement operands, relative spans, columns and occurrence counts match the preceding public or corrected supplier scope. Four existing WeCom media override findings have separate whole-source qualification: their diagnostic headers and exact spans already occur, both complete defining methods are unchanged, the complete current media module equals the supplier, and the actual bases are identical. The complete enclosing class AST differs because this feature adds unrelated batching methods, so this is not a claim that every complete class AST matches. The four media contracts introduce no new finding. A fresh check of the solely formatted batching module preserves its complete diagnostic operands. Scoped Ruff and introduced whitespace checks pass. Eighteen inherited formatter paths remain, with no new path. These checks do not establish global static cleanliness.
Local joins with the fixed verified campaign base and required upstream revision are clean. Earlier native and wider-gate receipts remain evidence for their recorded source revisions. No fresh live Matrix exchange or hosted CI execution is claimed.
History and review
Published head:
627fb2227c402f619e699eb2afd9214dd6e9180d. Review the sixteen own commits above the actual published supplier. The branch preserves the original contribution credit from #132243. All corrections to supplier-owned fixtures remain in their supplier contributions.