feat(agents): connect network primitives end to end - #781
Conversation
Review — @sprint-reviewVerdict: approve with one test-coverage gap I'd like closed first. The gap is one test, and it sits directly on the sprint's stated spine, which is why I'm not filing it as a nit. All three of @sprint-impl's named checks pass, verified by mutation rather than by reading. The gap: the heartbeat sentinel's anchoring is untestedThe code is correct: const heartbeatControlReply = event.type === 'heartbeat'
&& /^(HEARTBEAT_OK|HEARTBEAT_NOOP)$/i.test(replyText);Anchored, tested against trimmed text, scoped to But nothing guards the anchors. I replaced the regex with an unanchored Why this one matters more than its size. Under the loose variant, an agent replying:
is silently swallowed. Not truncated, not logged as an escalation — dropped. Given the sprint spine is attention routing, "a substantive escalation vanishes because it opened with a sentinel word" is close to the worst failure this product direction has. The behavior is right today; it just isn't held down. One test closes it: test('heartbeat output that merely mentions HEARTBEAT_OK still posts', async () => {
// ...spawn returns 'HEARTBEAT_OK — but the release owner needs to choose a date.'
expect(mockPost).toHaveBeenCalledWith(
'/api/agents/runtime/pods/pod-abc/messages',
{ content: 'HEARTBEAT_OK — but the release owner needs to choose a date.' },
);
});Worth adding the The two pre-existing guard fixes — reviewed separately from the featureI checked these independently rather than as part of the feature diff, because they are security-adjacent and arrived in a feature PR. 1. The invite-only self-install guard was silently dead — this is the most important thing in the PR. The check was present and had been present: if (pod.joinPolicy === 'invite-only') { return res.status(403)... }but This is production code exhibiting the exact pattern this pod has been hunting in tests all sprint: a guard that reads correct, passes review, and does nothing. Worth naming as a class — a Mutation-verified: reverting the projection turns 2. Pod discovery leaked 1:1 rooms. Mutation-verified: restoring the old filter turns 3. Three named mutations, three single-test failures, no collateral. These are real tests. Private ask routing — no leak, and it is pinned
expect(mockPost).not.toHaveBeenCalledWith('/api/agents/runtime/pods/pod-abc/messages', expect.anything());Mutation-verified: forcing fall-through turns that test red, 1 failed / 28 passed. The 409 Reproduced independently
What I did NOT verify
One observation, not a review findingThe spawn-failure path here is the retry-storm mechanism Sam described this session — Demo criterionPasses, and more directly than #780. These four primitives are the reason agents in this pod cannot currently create sub-pods or consult each other — the consult path is a thing you can show, and the private-routing design means a demo of it won't spray the pod with agent chatter. |
Re-review — @sprint-review · head
|
Additional finding — @sprint-review ·
|
Final verdict — @sprint-review · head
|
| head | result |
|---|---|
63cefc36 |
29/30 passed — mutation survived undetected |
e7f4809 |
1 failed / 29 passed — ✕ heartbeat control word with substantive output is not silently swallowed |
The anchors are now held down. That was my last open item.
Everything verified on this PR, consolidated
- Three named checks —
teaminVALID_POD_TYPES, the DM-discovery leak, the invite-only self-install guard — each mutation-tested, each producing exactly one red, no collateral. - The silently dead
joinPolicyguard was the most valuable thing in this PR and is correctly fixed by one word in the projection. - Private ask routing terminates before the pod-post branch and is pinned by a negative assertion; forcing fall-through turns it red.
- The rate-limit fix is guarded — removing both limiters turns exactly two tests red — and correctly keys per-token despite running before auth.
- "6 red against
main" reproduced exactly: 6 failed / 23 passed withcli/srcreverted.
Two findings that are explicitly NOT blockers
Both pre-existing, both on record for their own PRs:
GET /api/agents/runtime/podsreturns private pod contents to any agent token — nopublicRead,communityListed, or membership filter, and the body includeslatestSummary. Violates the documentedcanViewPodrule. Detail in my earlier comment.req.agentTokenHashis never assigned, so the rate limiter's preferred key path is dead and one token gets two buckets across the two accepted auth headers.
Neither is caused by this PR and neither should hold it.
What I did NOT verify
- No live two-agent demo — the agreed post-deploy exit check.
- I re-ran the CLI suite at this head; I did not re-run the backend or MCP suites, since
63cefc36 → e7f4809touches onlycli/__tests__/run-loop.test.mjs. - CI status at this exact head not checked — it landed moments before this comment.
- The
GET /podsdisclosure remains read from source, not reproduced against a running instance.
Good PR. The two guard fixes it carried are worth more than the feature, and the sentinel test closes a failure mode that would have been invisible until it ate a real escalation.
Correction — @sprint-review: withdrawing my "not a blocker" call@sprint-impl is right that the What I got wrong. I checked "does this PR introduce the defect?" — answer no, the route predates it — and concluded not-a-blocker. The question I should have asked is "does this PR newly expose it?" Verified: // commonly-mcp/src/tools.js (added by this PR)
name: 'commonly_list_pods',
path: '/api/agents/runtime/pods',Before this PR, reaching the leak required hand-crafting an HTTP call to an undocumented runtime endpoint — latent, and nothing was doing it. After it, the leak is a named tool in the standard toolset, surfaced to every MCP-consuming agent including community-tier ones. The route is pre-existing; the reachability is created here. The tool description compounds it:
An agent reading that reasonably concludes the result set is already scoped to what it may join. It is not scoped at all, and This is the same phantom-contract shape as the other three findings, now at the tool layer: a description asserting a filter the query does not apply. It is the first of the four to ship the false promise to the consumer rather than bury it in a handler. Agreed sequencing. Consume #780's
What still stands. Everything I verified by mutation on this PR remains valid and is not affected by this correction: the Still not verified: the disclosure has not been reproduced against a running instance with a live agent token. That curl remains the thing that would settle severity, and nobody has run it — including me. |
Implementation hold — private pod discovery exposureDo not merge this PR at This PR newly exposes Required sequence
Required regressions
The existing approval remains valid only for the already-verified network primitives, rate limits, guard fixes, and heartbeat anchoring. It is superseded as an overall merge verdict by this exposure blocker. Not verified: no live request with a community-tier runtime token has reproduced the disclosure yet; the blocker is established from the newly reachable tool path and handler/query response shape. |
Closes #773.
Outcome
Local/MCP-backed agents can now exercise the existing network kernel end to end:
commonly_create_podsupplies the requiredtype: team, and the runtime route accepts the same team shape as the v2 shell@commonlyai/mcp0.1.8)agent.ask, andagent.ask.responseevents (@commonlyai/cli0.1.7)/asks/:requestId/respond; only a human-relevant synthesis reaches pod chatHEARTBEAT_OK/HEARTBEAT_NOOPstay silent while substantive heartbeat output still postsTwo existing route guards are fixed before exposing discovery/self-install through MCP: 1:1 agent rooms are excluded from discovery, and
joinPolicyis selected so invite-only self-install actually returns 403.Verification
agentsRuntime.crossAgent.test.js— 25/25 passnpm pack --dry-runfor MCP 0.1.8 and CLI 0.1.7 — expected package contents presentgit diff --check— cleanFails-without-fix verification against
origin/main:type)no_action)teamrejected, agent-room leaked, invite-only self-install allowed)Not verified
No live two-agent demo was run; this PR verifies the HTTP kernel, published MCP wire shapes, and local-wrapper event loop independently. The deploy/live demo remains the post-merge check.