Skip to content

fix(announce): propagate sender + role through team-task announce re-ingress#1128

Open
mozaa-solana wants to merge 1 commit intonextlevelbuilder:devfrom
mozaa-solana:fix/team-task-announce-sender-propagation
Open

fix(announce): propagate sender + role through team-task announce re-ingress#1128
mozaa-solana wants to merge 1 commit intonextlevelbuilder:devfrom
mozaa-solana:fix/team-task-announce-sender-propagation

Conversation

@mozaa-solana
Copy link
Copy Markdown

Summary

When a member completes a team task, goclaw fans the result back to the Lead's session via the team-task announce queue. The Lead resumes a turn whose initial user message is the synthesized [System Message] Team member ... completed task. Result: ... string.

Inside that resumed turn, the Lead can call any tool, including `write_file` and `cron` mutations. In group-scope sessions, those tools route through `CheckFileWriterPermission` / `CheckCronPermission`, which deny when the resumed RunRequest carries no SenderID:

permission denied: system context cannot write files in group chats. If this is a legitimate user action, ensure the acting sender is preserved through the tool chain.

`team_tool_dispatch.go` already stamps `MetaOriginSenderID` + `MetaOriginRole` into the dispatch metadata. `consumer_handlers.go` already reads inMeta on the completion-side teammate message. The gap was in between:

  • `announceRouting` had no field for `OriginSenderID` / `OriginRole`
  • The RunRequest it built had no `SenderID` / `Role` set
  • `loop_context.injectContext` skips `WithSenderID` when `req.SenderID` is empty — so the Lead's resumed ctx had no sender attribution, and group-scope permission checks tripped the deny path.

The subagent announce path (`subagentAnnounceRouting`) already had these fields wired since #915. This brings the team-task path to parity.

Failure repro

  1. Human pings Lead in a group chat → Lead dispatches task to a member with `team_tasks(action="create", ...)`
  2. Member completes via `team_tasks(action="complete", ...)`
  3. Lead's session resumes, processes the announce, calls `write_file` to save a log/output
  4. Returns: `permission denied: system context cannot write files in group chats`

Changes

  • `cmd/gateway_announce_queue.go`: add `OriginSenderID` + `OriginRole` to `announceRouting`; pass them into the RunRequest.
  • `cmd/gateway_consumer_handlers.go`: read `MetaOriginSenderID` + `MetaOriginRole` from inMeta when building the routing struct.
  • `cmd/gateway_announce_routing_test.go` (new): 2 unit tests guarding both "real human propagates through" and "empty stays empty" cases.

No security relaxation

Empty upstream → still empty downstream (still denies, as intended for genuine system-initiated turns). Only legitimately-attributed dispatches now flow through.

Test plan

  • `go build ./...` clean
  • `go test ./cmd/... ./internal/tools/...` passes (existing + new)
  • Reproduced the failure on c02 (production deploy of mozaa-solana fork), applied this patch, retested same Lead+member dispatch in group chat — `write_file` now succeeds with real sender attributed.

Related

Follow-up to #915 / #1105 (sender propagation through MCP bridge + delegate). Same class of bug, last remaining code path that lost attribution. The c02-choros team had a Lead agent (Neo) consistently failing to log cycle results because of this — diagnostic message in `config_permission_store` was the trail.

…ingress

When a member calls team_tasks(action="complete"), goclaw fans the result
back to the Lead's session via the team-task announce queue. The Lead
resumes a turn whose initial user message is the synthesized
"[System Message] Team member ... completed task. Result: ..." string.

Inside that resumed turn, the Lead is a regular agent — it can call any
tool, including write_file and cron mutations. Those tools route through
CheckFileWriterPermission / CheckCronPermission, which in group-scope
sessions deny when the resumed RunRequest carries no SenderID:

    permission denied: system context cannot write files in group chats.
    If this is a legitimate user action, ensure the acting sender is
    preserved through the tool chain.

team_tool_dispatch.go already stamps MetaOriginSenderID and MetaOriginRole
into the dispatch metadata. consumer_handlers.go already reads inMeta on
the completion-side teammate message. The gap was in between:

  - announceRouting (cmd/gateway_announce_queue.go) had no field for
    OriginSenderID / OriginRole
  - The RunRequest it built had no SenderID / Role set
  - loop_context.injectContext skips WithSenderID when req.SenderID is
    empty — so the Lead's resumed ctx had no sender attribution, and
    every group-scope permission check then tripped the deny path.

Subagent path (subagentAnnounceRouting in gateway_subagent_announce_queue.go)
already had these fields wired since nextlevelbuilder#915. This brings the team-task
path to parity. No security relaxation: empty upstream → still empty
downstream (still denies, as intended for genuine system-initiated
turns); only legitimately-attributed dispatches now flow through.

  - cmd/gateway_announce_queue.go: add OriginSenderID + OriginRole to
    announceRouting; pass them into the RunRequest.
  - cmd/gateway_consumer_handlers.go: read MetaOriginSenderID +
    MetaOriginRole from inMeta when building the routing struct.
  - cmd/gateway_announce_routing_test.go: 2 unit tests guarding both
    "real human propagates through" and "empty stays empty" cases so
    this regression can't re-land silently.

Tests: existing cmd/ + internal/tools/ suites pass; new tests pass.
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