Repository navigation
feat(addressing): agents name a role, the platform resolves the person — reports and messages (abilityai/trinity-enterprise#606) - #3050
Conversation
…n — reports and messages (Abilityai/trinity-enterprise#606) The ent#606 ruling (prds/permission-layers.md §5a): every outbound object an agent produces for a human carries `to:` — primary | approver | viewer | operator — and the platform resolves the person through the agent's assignments. #611 (#3028) built it for asks; this extends it to reports and messages through ONE shared rule. - services/role_addressing.resolve: a provider's people_for answer first, else the core defaults (primary → owner, operator → nobody, approver/viewer refused). No primary assigned → operator. Refusals are named (invalid_to, role_unassigned). ask_service._address now delegates to it; asks are unchanged (#3028's tests pass as they were). - Reports: ReportCreate.to. - operator, nobody, or the owner → operator-only (the owner reads the operator surface). - one other person → the checked audience; off the roster → 422 role_unreachable. - several people → 422 role_resolves_to_several. - to + audience_email → 422 addressing_conflict. - Neither → operator-only, unchanged. - Messages: exactly one of `to` (primary | approver | viewer) or recipient_email. A role nobody fills or several fill is a named 422; operator is not a message recipient (use an ask_operator alert). - MCP report / send_message take `to` and echo the role, never the resolved person. - Agent-supplied emails still work for two releases, logged as deprecated. The provider side (people_for over role assignments) ships separately in the private module. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
c73d956 to
c3c6327
Compare
obasilakis
left a comment
There was a problem hiding this comment.
Ran /review and /validate-pr on this PR. No blocking findings. Five minor items below.
Evidence
test_ent606_role_addressing.py: 27/27 pass. Adjacent suites (ent611 asks, ent364, ent428, ent365 report audience, #918 report endpoint, message access gate, proactive rate-limit/toggle, models guard): 431 pass.- Mutations: 7 of 9 go red. That covers the four in the PR body plus three more:
role_unreachableremoved,addressing_conflictremoved, and provider lookup bypassed. The two survivors are items 3 and 4 below. - Security greps on the diff are clean: only
@example.comaddresses, and no IPs, host paths, env vars or mode changes.
Findings
- Private file path in public repo.
services/role_addressing.py(module docstring) anddocs/memory/requirements/security.md§26.11 citeprds/permission-layers.md§5a. That file is not in this repo, and nothing ondevreferenced it before. The enterprise-docs guard pattern passes it, but CLAUDE.md bars references to private repos. Suggest citing ent#606 only. - Resolved email can reach the agent on a failed role-addressed message. When
toresolves to a person and delivery fails, the proactive service's error detail includes that person's email:proactive_message_service.pyNotAuthorizedError (~L270) and the Telegram/Slack/WhatsApp RecipientNotFoundError messages (~L478, ~L592, ~L619). The router passesstr(e)through as the 403/404 detail. The MCPsend_messagetool replaces 403/404 with generic text, so this is reachable only through a direct HTTP call with the agent's key. It still conflicts with the rule that an agent sees the role and never the person. Option: whenrequest.tois set, return a role-only detail for 403/404. - Dead branch in
ask_service._address. Theinvalid_tomapping never runs, becausecreate's validation atask_service.py:507rejects unknown roles earlier. A mutation disabling it leaves every test green. Drop it, or keep it and add a comment saying it is defensive. - Deprecation log asymmetry. Reports log email addressing only when
current_user.agent_nameis set. Messages log it for every caller, and the message-side log has no test (the mutation removing it survives). - No removal ticket for the deprecation.
audience_email/recipient_emailare said to keep working "for two releases". A follow-up issue for removing them would keep that from being lost.
Process notes
Fixes abilityai/trinity-enterprise#606points at the other tracker, so the issue will not auto-promote or auto-close. It needsstatus-in-devset by hand after merge and a manual close at release./cso --diffhas not run. The pipeline table asks for it on P1 features.
…Abilityai/trinity-enterprise#606) Review follow-ups on PR #3050: - A role-addressed message that fails authorization (403) or delivery (404) now returns a role-only detail (role_not_opted_in / role_no_channel); the service's text names the resolved email, so it passes through only for email-addressed sends. - The message-side email-addressing deprecation log fires only for agent keys, matching reports. - Drop the unreachable invalid_to mapping in ask_service._address; its only caller passes a role already validated against ASK_ROLES. - Cite trinity-enterprise#606 only, not a private design file. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Thanks @obasilakis — all five addressed in fbab7e4 (on top of a merge of current 1. Private file path — 2. Resolved email on a failed role-addressed message — fixed at the router. When
Two notes. WhatsApp is not reachable here: the request model's 3. Dead 4. Deprecation log asymmetry — messages now log only when 5. Removal ticket — filed #3087 ( Tests (
Mutations (each reverted after):
Process
🤖 Generated with Claude Code |
|
merge-train: not on this train — validated READY (five review follow-ups verified; 407 passed), but it conflicts in One question for you before then: with |
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…t's create response (ent#606)
merge-train mechanical fix: POST /api/agents/{name}/reports returned
addressed_to (the resolved person's email) to an agent-key caller. Route it
through _hide_audience like the read routes, and assert the response body.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
merge-train: this PR is on the train. I pushed one mechanical fix to your branch (
Not blocking, but worth a follow-up: no TypeScript test runs |
vybe
left a comment
There was a problem hiding this comment.
merge-train: validated (validate-pr + review, mechanical fixes noted on the PR); merged individually — batch under 4, no train assembled.
Fixes abilityai/trinity-enterprise#606. It's a cross-tracker issue, so it closes by hand at release.
What
The ent#606 ruling says every outbound object an agent produces for a human names a role, never a person:
to: primary | approver | viewer | operator, which the platform resolves. #3028 built that for asks. This PR extends it to reports and messages through one shared rule, so an ask and a report addressed to the same role reach the same people.services/role_addressing.resolvepeople_foranswers first. The core defaults apply otherwise:primary→ the owner;operator→ nobody recorded;approverandviewer→ refused.invalid_to,role_unassigned.ask_service._addressnow delegates to this rule. Asks are unchanged: feat(operator-queue): agents raise asks natively — ask_operator, receipts, re-asks (abilityai/trinity-enterprise#611, PR B) #3028's ask tests (337) pass as they were, and the owner lookup is stillask_service._owner_email, so their patches still apply.Reports
ReportCreate.to:toresolves tooperator, nobody, or the agent's ownerrole_unreachablerole_resolves_to_several(a report has one reader)totogether withaudience_email→ 422addressing_conflict.Messages
SendMessageRequesttakes exactly one ofto(primary | approver | viewer) orrecipient_email.operatoris not a message recipient. The operators' door is anask_operatoralert.MCP
reportandsend_messageaccepttoand echo the role, never the resolved person.Compatibility
audience_email/recipient_emailstill work for two releases. Each use is logged as deprecated once per agent and surface.The provider side (
people_forover role assignments) is abilityai/trinity-enterprise#721, stacked on the assignments PR.Verification
tests/unit/test_ent606_role_addressing.py: 27 tests.dev(environment-dependent IPv4-mapped IPv6 SSRF cases).tsc --noEmiton the MCP server is clean.Docs:
architecture/backend.md(the service row);feature-flows/agent-reports.mdandproactive-messaging.md(an "Addressed by role" section in each);requirements/security.md§26.11.🤖 Generated with Claude Code