Skip to content

feat(channel): propagate sender user ID into channel system prompt#5526

Merged
theonlyhennygod merged 1 commit intozeroclaw-labs:masterfrom
sunriseapps:feat/mattermost-user-id-in-prompt-master
Apr 8, 2026
Merged

feat(channel): propagate sender user ID into channel system prompt#5526
theonlyhennygod merged 1 commit intozeroclaw-labs:masterfrom
sunriseapps:feat/mattermost-user-id-in-prompt-master

Conversation

@titulus
Copy link
Copy Markdown
Contributor

@titulus titulus commented Apr 8, 2026

Summary

  • Base branch target (master for all contributions): master
  • Problem: Mattermost sender IDs are available in runtime, but they are not propagated into the channel system prompt, so the model can distinguish channels/threads but not the user who addressed the bot.
  • Why it matters: Future RBAC and user-scoped behavior need the bot to distinguish one Mattermost user from another by internal platform ID.
  • What changed: Added sender to the channel system prompt context, passed msg.sender at prompt build time, and added regression tests plus a live Mattermost verification.
  • What did not change (scope boundary): No Mattermost discovery/polling/listener scoping changes, no config contract changes for this feature, no reply routing changes, and no unrelated architectural expansion.

Label Snapshot (required)

  • Risk label (risk: low|medium|high): risk: medium
  • Size label (size: XS|S|M|L|XL, auto-managed/read-only): auto
  • Scope labels (core|agent|channel|config|cron|daemon|doctor|gateway|health|heartbeat|integration|memory|observability|onboard|provider|runtime|security|service|skillforge|skills|tool|tunnel|docs|dependencies|ci|tests|scripts|dev, comma-separated): channel,tests
  • Module labels (<module>: <component>, for example channel: telegram, provider: kimi, tool: shell): channel: mattermost
  • Contributor tier label (trusted contributor|experienced contributor|principal contributor|distinguished contributor, auto-managed/read-only; author merged PRs >=5/10/20/50): auto
  • If any auto-label is incorrect, note requested correction: N/A

Change Metadata

  • Change type (bug|feature|refactor|docs|security|chore): feature
  • Primary scope (runtime|provider|channel|memory|security|ci|docs|multi): channel

Linked Issue

  • Closes #N/A
  • Related #N/A
  • Depends #N/A
  • Supersedes #N/A

Supersede Attribution (required when Supersedes # is used)

  • Superseded PRs + authors (#<pr> by @<author>, one per line): N/A
  • Integrated scope by source PR (what was materially carried forward): N/A
  • Co-authored-by trailers added for materially incorporated contributors? (Yes/No): No
  • If No, explain why (for example: inspiration-only, no direct code/design carry-over): not superseding any PR
  • Trailer format check (separate lines, no escaped \n): (Pass/Fail): Pass

Validation Evidence (required)

Commands and result summary:

cargo fmt --all -- --check
CARGO_BUILD_JOBS=1 cargo clippy --all-targets -- -D warnings
CARGO_BUILD_JOBS=1 cargo test
cargo build --release
  • Evidence provided (test/log/trace/screenshot/perf): local command output; targeted unit tests for prompt construction; live Mattermost DM verification against the local debug daemon
  • If any command is intentionally skipped, explain why: none

Security Impact (required)

  • New permissions/capabilities? (Yes/No): No
  • New external network calls? (Yes/No): No
  • Secrets/tokens handling changed? (Yes/No): No
  • File system access scope changed? (Yes/No): No
  • If any Yes, describe risk and mitigation: N/A

Privacy and Data Hygiene (required)

  • Data-hygiene status (pass|needs-follow-up): pass
  • Redaction/anonymization notes: platform-specific sender IDs are now intentionally included in model prompt context for channel runs; no tokens or additional profile fields are injected; live-test secrets are not included in repo changes or PR text
  • Neutral wording confirmation (use ZeroClaw/project-native labels if identity-like wording is needed): confirmed

Compatibility / Migration

  • Backward compatible? (Yes/No): Yes
  • Config/env changes? (Yes/No): No
  • Migration needed? (Yes/No): No
  • If yes, exact upgrade steps: N/A

i18n Follow-Through (required when docs or user-facing wording changes)

  • i18n follow-through triggered? (Yes/No): No
  • If Yes, locale navigation parity updated in README*, docs/README*, and docs/SUMMARY.md for supported locales (en, zh-CN, ja, ru, fr, vi)? (Yes/No): N/A
  • If Yes, localized runtime-contract docs updated where equivalents exist (minimum for fr/vi: commands-reference, config-reference, troubleshooting)? (Yes/No/N.A.): N/A
  • If Yes, Vietnamese canonical docs under docs/i18n/vi/** synced and compatibility shims under docs/*.vi.md validated? (Yes/No/N.A.): N/A
  • If any No/N.A., link follow-up issue/PR and explain scope decision: N/A

Human Verification (required)

What was personally validated beyond CI:

  • Verified scenarios: coding agent sent a live Mattermost DM to the bot from user nhkmck65yf8tir6p618b1wac1y; the bot replied with that exact sender ID; human repeated with a second prompt and received the same correct ID
  • Edge cases checked: prompt includes sender= when reply_target is present; prompt omits channel context when reply_target is empty; different senders produce different prompts
  • What was not verified: provider-specific downstream handling of sender IDs outside the tested Mattermost flow; behavior on every non-Mattermost channel integration

Side Effects / Blast Radius (required)

  • Affected subsystems/workflows: channel system prompt construction; channel-driven LLM runs where reply_target is present; Mattermost runtime behavior verified live
  • Potential unintended effects: models may reference sender IDs more explicitly in replies; prompt context changed for other channel integrations that use the same helper
  • Guardrails/monitoring for early detection: focused unit tests on prompt content and live Mattermost verification against the local daemon

Agent Collaboration Notes (recommended)

  • Agent tools used (if any): repository search/read tools, terminal validation, browser inspection, Mattermost API workflow for live verification
  • Workflow/plan summary (if any): traced Mattermost message flow, confirmed msg.sender already existed in runtime, patched prompt construction at the root cause, added regression tests, ran full local validation, then verified live in Mattermost
  • Verification focus: sender ID propagation into prompt and observable bot behavior in Mattermost
  • Confirmation: naming + architecture boundaries followed (AGENTS.md + CONTRIBUTING.md): Yes

Rollback Plan (required)

  • Fast rollback command/path: revert commit 11da7df1
  • Feature flags or config toggles (if any): none
  • Observable failure symptoms: channel replies unexpectedly mention sender IDs, or prompt-sensitive behavior changes in channel conversations

Risks and Mitigations

  • Risk: prompt contract changed for channel runs that include reply_target, not just Mattermost

  • Mitigation: kept the change minimal and localized to the existing context block; added regression tests that pin the new behavior

  • Risk: platform user IDs are now forwarded to the model provider as part of prompt context

  • Mitigation: only the existing platform sender ID is added, with no secrets or extra profile metadata; this is required for user distinction and future RBAC work

Include the platform-specific sender ID (e.g. Mattermost user_id) in
the channel system prompt so the LLM can identify and distinguish
individual users. This lays the groundwork for future RBAC-based
access control by making the sender identity available to the model.

Changes:
- Add sender parameter to build_channel_system_prompt()
- Inject sender=<id> into the Channel context block
- Pass msg.sender at the call site in process_channel_message()
- Add three unit tests for the new behavior
@github-actions github-actions bot added the channel Auto scope: src/channels/** changed. label Apr 8, 2026
Copy link
Copy Markdown
Collaborator

@theonlyhennygod theonlyhennygod left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comprehension Summary

What: This PR adds the existing msg.sender field (platform-specific user ID) to the channel system prompt by threading a new sender parameter through build_channel_system_prompt(). The sender ID is injected into the "Channel context" block alongside the existing channel and reply_target fields. Three regression tests are added.

Why: Mattermost (and other channel) sender IDs are available at runtime but were not propagated into the LLM prompt context. This prevents the model from distinguishing which user sent a message — a prerequisite for future RBAC and user-scoped behavior.

Blast radius: The prompt text changes for all channel integrations where reply_target is non-empty, not just Mattermost. The sender field is already populated by every channel implementation (ChannelMessage.sender in traits.rs), so no upstream changes were needed. Models may begin referencing sender IDs more explicitly in replies.


Review Summary

Gate Checks — All Pass

  • Template: Fully completed, all required sections present.
  • CI: 20/20 checks pass (Lint, Test, Build x3, Security Audit, Strict Delta Lint, Benchmarks, Docs Quality, all gates).
  • Size: XS (1 file, +40/−3). Appropriate scope.
  • Privacy: Test fixtures use neutral placeholders (user_abc123, user_aaa, user_bbb). No PII, credentials, or identity leakage. Pass.
  • Duplicates: No overlapping open PRs (other Mattermost PRs address unrelated features).
  • Architecture: No new dependencies, no trait bypass, no config changes. Uses the existing ChannelMessage.sender field already populated by all channel implementations.
  • Supersedes/Attribution: N/A.

Code Review — No Issues Found

  • The sender parameter is correctly threaded through the function signature and call site.
  • Sender injection is gated behind the existing !reply_target.is_empty() check, so it only appears when channel context is relevant.
  • No unwrap() in library code. No unnecessary allocations — the sender is interpolated into the existing format!() call.
  • Naming follows project conventions (sender, not user_id or author).
  • The prompt text addition is clear and well-structured.

Regression Analysis

  • build_channel_system_prompt has exactly one call site (process_channel_message, line 2744). No other callers are affected.
  • All channel implementations already populate msg.sender, so no channel is broken by the new parameter.
  • The prompt change applies to all channels with non-empty reply_target, not just Mattermost. The contributor explicitly acknowledges this in blast radius and has mitigated via targeted tests.

Test Coverage

  • Three new unit tests cover: (1) sender ID inclusion in prompt, (2) sender omission when reply_target is empty, (3) different senders produce different prompts.
  • Tests are well-named, focused, and use neutral placeholders.

Security Assessment

No security impact identified. Platform-internal sender IDs (e.g., Mattermost user_id) are opaque identifiers, not secrets. They are already present in the runtime and are now additionally forwarded to the model provider as prompt context. No tokens, profile metadata, or credentials are exposed.

Performance Assessment

No performance impact identified. The change adds one string interpolation to an existing format!() call in a non-hot path (prompt construction happens once per channel message).


Thank you @titulus for a clean, well-scoped contribution with thorough validation evidence and thoughtful blast-radius documentation. This PR is ready for maintainer merge.

@theonlyhennygod theonlyhennygod added the agent-approved PR approved by automated review agent label Apr 8, 2026
@theonlyhennygod theonlyhennygod merged commit 33710e7 into zeroclaw-labs:master Apr 8, 2026
20 checks passed
@github-project-automation github-project-automation bot moved this from Backlog to Done in ZeroClaw Project Board Apr 8, 2026
5queezer added a commit to 5queezer/hrafn that referenced this pull request Apr 10, 2026
Port of zeroclaw-labs/zeroclaw#5526. The sender's platform-specific user
ID is now included in the channel context block of the system prompt when
reply_target is present, enabling the model to distinguish between
different users in channel conversations.

Co-authored-by: titulus <titulus@users.noreply.github.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
5queezer added a commit to 5queezer/hrafn that referenced this pull request Apr 11, 2026
)

* feat(channel): propagate sender user ID into channel system prompt

Port of zeroclaw-labs/zeroclaw#5526. The sender's platform-specific user
ID is now included in the channel context block of the system prompt when
reply_target is present, enabling the model to distinguish between
different users in channel conversations.

Co-authored-by: titulus <titulus@users.noreply.github.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix(channel): sanitise metadata before prompt interpolation

Strip newlines and carriage returns from channel_name, reply_target,
and sender before injecting into the system prompt to prevent
control characters from breaking prompt structure.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: titulus <titulus@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
lzc256 pushed a commit to lzc256/zeroclaw that referenced this pull request Apr 11, 2026
…eroclaw-labs#5526)

Include the platform-specific sender ID (e.g. Mattermost user_id) in
the channel system prompt so the LLM can identify and distinguish
individual users. This lays the groundwork for future RBAC-based
access control by making the sender identity available to the model.

Changes:
- Add sender parameter to build_channel_system_prompt()
- Inject sender=<id> into the Channel context block
- Pass msg.sender at the call site in process_channel_message()
- Add three unit tests for the new behavior
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

agent-approved PR approved by automated review agent channel Auto scope: src/channels/** changed.

Projects

Status: Shipped

Development

Successfully merging this pull request may close these issues.

2 participants