Skip to content

🤖 refactor: extract the agent peer-messaging broker from TaskService - #4010

Merged
ibetitsmike merged 3 commits into
mainfrom
mike/arch-peer-message-broker
Aug 30, 2026
Merged

ibetitsmike merged 3 commits into
mainfrom
mike/arch-peer-message-broker

Conversation

@ibetitsmike

@ibetitsmike ibetitsmike commented Aug 29, 2026 •

Copy link
Copy Markdown
Contributor

Summary

Extracts the peer/family agent-messaging protocol's admission invariants out of TaskService into a new AgentPeerMessageBroker: sliding-window rate limits (per sender-target pair and per target), duplicate suppression, queued-peer-message capacity, consecutive-peer-wake caps, per-pair/per-target session budgets with idempotent refunds, per-target delivery locks, title capping, and peer envelope/trigger composition. TaskService keeps tree topology, stop-epoch/overlay orchestration, and dispatch, and delegates every admission decision directly (no pass-through wrappers).

Background

Refactor #4 from the 2026-08-29 architecture review (evidence at main @ f04e0f8): taskService.ts inlined the whole protocol across two regions (~5654-6736, ~9612-10097), and every protocol-invariant test had to spin up the 35-method WorkspaceHost mock harness. Protocol invariants now live in one module testable with a fake one-method host and a mock clock.

Behavior-preserving by construction: check ordering, refusal codes, reason strings, retryAfterMs math, and budget charge arithmetic are byte-identical to the pre-refactor code.

Net LOC delta

added removed net
Production (agentPeerMessageBroker.ts + taskService.ts) 271 286 -15
Tests (agentPeerMessageBroker.test.ts + taskService.test.ts) 238 561 -323
Total 509 847 -338

Harness-heavy tests that only re-asserted pure invariants through the full TaskService stack were deleted and replaced by table-driven broker tests; one thin delegation test per route (peer rate-limit refusal, task_message_parent and sibling budget exhaustion) plus every test covering behavior the broker cannot see (refund-on-dispatch-failure, charge-at-admission ordering, stop races, envelope-row persistence, delivery serialization) were kept. The only irreducible addition is the broker's structural boilerplate (imports, class/constructor, the local one-method host interface), which is what makes the invariants independently testable.

The broker declares its own minimal host interface (countQueuedAgentPeerMessages) instead of importing from taskWorkspaceSeam.ts, keeping this diff merge-independent from the parallel WorkspaceHost role-interface split.

Validation

  • Remote dogfood UAT (Coder Agents): PASS on ae9bf3e93 — live Xum UI multi-agent tree across descendant/sibling/ancestor relations, an independent 22-assertion edge-case probe (rate/dedupe/wake/budget boundaries at exact ceilings, refund idempotence), and a source-level cross-check of every refusal string and retry computation against f04e0f8. The one commit since (daa28db4f) is cosmetic (test-cast type reuse), revalidated locally.
  • bun test src/node/services/agentPeerMessageBroker.test.ts: 12/12.
  • bun test src/node/services/taskService.test.ts: 514 pass; the 2-3 sporadic failures (terminal recovery ... x2, higher-ancestor waiters ...) reproduce identically on pristine origin/main in this environment (config.json atomic-write chown/rename ENOENT under /tmp) and are unrelated to this diff.
  • make static-check: green.

Risks

Low. The moved logic is byte-identical and the delegation points are 1:1 at existing callsites; the riskiest surface (admission/charge ordering under concurrent sends) is covered by kept integration tests and the broker's check-order test (queued-count host query is only reached after in-memory checks pass).


Generated with xum • Model: anthropic:claude-fable-5 • Thinking: xhigh • Cost: $37.96

Stack

Layer 3/10 of the architecture refactor stack (net -5,101 LOC overall). This PR's diff is only this layer, against mike/arch-git-patch-engine.

@chatgpt-codex-connector

This comment has been minimized.

@ibetitsmike

Copy link
Copy Markdown
Contributor Author

@codex review

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Keep them coming!

Reviewed commit: daa28db4f4

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@chatgpt-codex-connector

This comment has been minimized.

@ibetitsmike
ibetitsmike force-pushed the mike/arch-peer-message-broker branch from daa28db to dc16171 Compare August 30, 2026 16:38
@ibetitsmike
ibetitsmike changed the base branch from main to mike/arch-git-patch-engine August 30, 2026 16:39
@ibetitsmike

Copy link
Copy Markdown
Contributor Author

@codex review

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. 👍

Reviewed commit: dc161712c0

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@chatgpt-codex-connector

This comment has been minimized.

@ibetitsmike
ibetitsmike force-pushed the mike/arch-peer-message-broker branch from dc16171 to 686fa3c Compare August 30, 2026 21:10
Base automatically changed from mike/arch-git-patch-engine to main August 30, 2026 21:33
@ibetitsmike
ibetitsmike force-pushed the mike/arch-peer-message-broker branch from 686fa3c to 9c236c2 Compare August 30, 2026 21:33
@ibetitsmike
ibetitsmike added this pull request to the merge queue Aug 30, 2026
Merged via the queue into main with commit ad7f569 Aug 30, 2026
19 of 20 checks passed
@ibetitsmike
ibetitsmike deleted the mike/arch-peer-message-broker branch August 30, 2026 21:56
yermakoffivan pushed a commit to yermakoffivan/mux that referenced this pull request Aug 31, 2026
…eline (coder#4024)

## Summary

Unifies the four agent-tree message dispatch paths in `taskService.ts`
(descendant guidance, peer/ancestor `task_send_message`, and the
parent/sibling RLM family-message routes) behind one
relation-parameterized `sendTreeMessage` pipeline. The four public
methods keep their exact signatures, result/error shapes, and delivery
semantics; they are now thin wrappers that build a relation spec and
enter the shared pipeline.

## Background

Card 3 of the 2026-08-31 architecture review. PR coder#4010 centralized
peer-messaging admission invariants (throttles, budgets, envelope prep)
in `agentPeerMessageBroker.ts`, but the four dispatch paths still each
reimplemented the same ritual: validate topology, admission refusals,
envelope/payload construction, budget reservation, delivery lock,
dispatch, refund accounting. The same char-cap, budget, envelope, and
lock logic appeared up to four times with only the relationship check
differing (~1,150 lines).

## Implementation

- One private `sendTreeMessage(spec)` pipeline owns the shared phase
order (normalize/cap, topology, refusals, envelope, budget, lock,
dispatch, refund accounting), parameterized by a relation-discriminated
spec. Overloads keep each public route's result type exact.
- The trusted descendant machinery (queued-prompt splice, reactivation,
live guidance reservation) stays its own dispatcher under the pipeline;
it has no cap/budget/envelope by design, and the sibling family route
still dispatches through it with the shared parent as authorizing
ancestor.
- Parent and sibling family routes collapse into one
`sendFamilyTreeMessage` core; their envelope/trigger construction moves
into `AgentPeerMessageBroker.prepareFamilyMessage` next to
`preparePeerMessage`, so one module owns every envelope format.
Payload/trigger strings are byte-identical to before.
- Budget reservation/refund mechanics are unified
(`reserveTreeMessageBudget` with `markPersisted`/`refundIfUnpersisted`)
while each route keeps its exact refund policy (refund only when nothing
persisted; queued-then-cleared sends keep the charge).
- Peer admission machinery (sender/target liveness, stop-epoch latches,
`admissionStale` probe, dedupe keys, correlation metadata, wake
accounting) is preserved unchanged inside the peer leg.
- Duplicated per-route ritual tests (cap, budget refund, workflow
refusal, envelope framing) are consolidated into relation-parameterized
tests asserting through the public methods; unique-mechanics tests
(reactivation, guidance reservation cleanup, stop races, nuclear-family
topology) remain separate.

Net: -28 LOC across production and tests (+457/-485).

## Validation

- `make static-check` green; `make typecheck` green.
- `bun test` on `taskService.test.ts`, `agentPeerMessageBroker.test.ts`,
`tools/task_list.test.ts`,
`tests/ipc/tasks/persistentSubagentCompaction.test.ts`: fail set
identical to a clean-base run of the same suite (two pre-existing
host-specific fails, no regressions).
- Remote dogfood UAT exercised all four relations end to end on this
exact head (descendant steering, inactive-child reactivation, sibling
envelope + relationship tags, ancestor turn-end default, 16384-char peer
cap refusal, best-of candidate refusal). One High finding surfaced:
child-to-root sends that queue behind a busy root turn
(`queued`/`turn-end`) are never delivered after the turn ends. A
controlled re-run of the identical scenario against base main
`0672c8f47` reproduced it exactly, so it is a pre-existing defect in the
queued-wake drain path, not introduced by this refactor; it deserves a
separate fix.

## Risks

Intricate-logic refactor in the messaging hot path. Highest-severity
regression classes would be: lost or duplicated peer wakes
(budget/refund drift), envelope framing changes (security boundary), and
descendant reactivation semantics. Mitigations: phase order and refund
horizons preserved verbatim per route, envelope strings byte-identical,
admission probes untouched, and the relation-parameterized tests assert
through the public API with per-route error shapes.

---

_Generated with `xum` • Model: `anthropic:claude-fable-5` • Thinking:
`xhigh` • Cost: `$27.13`_

<!-- mux-attribution: model=anthropic:claude-fable-5 thinking=xhigh
costs=27.13 -->
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant