fix(grounding): allowlist channels the turn reached by tool #797

Merged
coilysiren merged 1 commit from aos/claude/bk79-grounding-tools into main 2026-08-15 15:35:37 +00:00
Member

Closes #794.

The defect

ValidateGrounding takes the turn's completed tool calls as executed and then builds the channel allowlist without them. The proof that #general was real sat unused in the function's own parameter list.

It is structural rather than a one-off. The deploy guardfiles fix each channel into its own tool path because restrict cannot glob snowflakes and the snowflakes share no prefix, so a channel's name lives in the tool name (list_general-message) and in the tool result, and never in supplied context. The allowlist could therefore essentially never contain a channel the lane reached by tool. Echo has the same shape with her eco- channels.

The fix

channelsReachedByTool derives the allowlist from completed tool names only. A tool name is generated from the guardfile, which is deploy-authored, reviewed, and fixed at image build, so this adds no attacker-controlled input. Tool results stay out: message bodies are community text, and a member posting #nonexistent would otherwise teach the check to accept a channel nobody has.

Outcome is not consulted. A failed call still proves the channel exists, and a reply reporting the failure names it correctly.

Acceptance

  • A reply naming a channel the turn read through a tool is not refused - TestValidateGroundingAllowsChannelsNamedByToolNames, covering the server-prefixed, hyphenated, get_ and bare-name forms the guardfiles produce.
  • A reply naming a channel that appears only in untrusted message content is still refused - TestValidateGroundingStillRefusesChannelsOnlyInToolResults.
  • A channel no tool named is still refused - TestValidateGroundingRefusesChannelsNoToolReached.
  • The exact roster summary from the issue comment now passes - TestValidateGroundingAcceptsTheRefusedRosterSummary.
  • The other three grounding rules are untouched, and the existing corpus and validator-language tests still pass.

ward gate is green: build, policy-check, vet, test, test-skips, pre-commit.

Not decided here

Whether the rule should cover channels named in supplied context that no tool touched, and whether a channel the bot cannot see should still be refusable. The issue calls both policy rather than plumbing, and this change leaves them alone.

Reasoning is written up in docs/sirens-echo-grounding-channels.md.

Closes #794. ## The defect `ValidateGrounding` takes the turn's completed tool calls as `executed` and then builds the channel allowlist without them. The proof that `#general` was real sat unused in the function's own parameter list. It is structural rather than a one-off. The deploy guardfiles fix each channel into its own tool path because `restrict` cannot glob snowflakes and the snowflakes share no prefix, so a channel's name lives in the **tool name** (`list_general-message`) and in the tool result, and never in supplied context. The allowlist could therefore essentially never contain a channel the lane reached by tool. Echo has the same shape with her `eco-` channels. ## The fix `channelsReachedByTool` derives the allowlist from completed **tool names only**. A tool name is generated from the guardfile, which is deploy-authored, reviewed, and fixed at image build, so this adds no attacker-controlled input. Tool results stay out: message bodies are community text, and a member posting `#nonexistent` would otherwise teach the check to accept a channel nobody has. Outcome is not consulted. A failed call still proves the channel exists, and a reply reporting the failure names it correctly. ## Acceptance * A reply naming a channel the turn read through a tool is not refused - `TestValidateGroundingAllowsChannelsNamedByToolNames`, covering the server-prefixed, hyphenated, `get_` and bare-name forms the guardfiles produce. * A reply naming a channel that appears only in untrusted message content is still refused - `TestValidateGroundingStillRefusesChannelsOnlyInToolResults`. * A channel no tool named is still refused - `TestValidateGroundingRefusesChannelsNoToolReached`. * The exact roster summary from the issue comment now passes - `TestValidateGroundingAcceptsTheRefusedRosterSummary`. * The other three grounding rules are untouched, and the existing corpus and validator-language tests still pass. `ward gate` is green: build, policy-check, vet, test, test-skips, pre-commit. ## Not decided here Whether the rule should cover channels named in supplied context that no tool touched, and whether a channel the bot cannot see should still be refusable. The issue calls both policy rather than plumbing, and this change leaves them alone. Reasoning is written up in `docs/sirens-echo-grounding-channels.md`.
fix(grounding): allowlist channels the turn reached by tool
All checks were successful
ci / test (pull_request) Successful in 43s
ci / publish-echo-image (pull_request) Has been skipped
ci / publish-observed (pull_request) Has been skipped
ci / image-build (pull_request) Successful in 4m30s
3fc982ffa7
The invented-channel rule built its allowlist from supplied context alone,
while the completed tool calls sat unused in the function's own signature.
The deploy guardfiles fix each channel into its own tool because `restrict`
cannot glob snowflakes, so a channel's name lives in the tool name and never
in supplied context. The allowlist could therefore essentially never contain
a channel the lane actually read, and a correct reply naming one was refused.

Tool names only. A tool result carries community text, so a member posting
`#nonexistent` would widen the guard to accept a channel nobody has.

Closes #794

Co-authored-by: Kai Siren <coilysiren@gmail.com>
Co-authored-by: Claude <noreply@anthropic.com>
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
coilyco-gaming/sirens-echo!797
No description provided.