56d869d2d9
MattermostAdapter.__init__, validate_mattermost_config, _standalone_send, and _handle_ws_event's mention-gating block all read MATTERMOST_URL/ MATTERMOST_REPLY_MODE/MATTERMOST_REQUIRE_MENTION/ MATTERMOST_FREE_RESPONSE_CHANNELS/MATTERMOST_ALLOWED_CHANNELS via raw os.getenv -- only MATTERMOST_TOKEN was already scoped via _get_scoped_secret. _apply_yaml_config additionally wrote MATTERMOST_REQUIRE_MENTION/MATTERMOST_FREE_RESPONSE_CHANNELS/ MATTERMOST_ALLOWED_CHANNELS into the process-global os.environ unconditionally (guarded only by `not os.getenv(...)`, first-writer-wins), the same apply_yaml_config_fn bug class already fixed for the Discord/Telegram/WhatsApp/DingTalk adapters in this series. Under gateway.multiplex_profiles, os.environ holds the DEFAULT profile's env-bridge output. A secondary profile with its own (or no) Mattermost config could silently connect to the default profile's server, thread its replies per the default profile's reply_mode, or -- since _handle_ws_event's mention-gating block runs on every LIVE inbound message, not just at construction -- have its require_mention/ free_response_channels/allowed_channels decisions driven by the default profile's settings for the adapter's entire runtime lifetime. Fix, mirroring the WhatsApp/DingTalk apply_yaml_config_fn pattern: - Add _profile_scoped_config_load() (same helper as DingTalk). - Rewrite _apply_yaml_config to skip the env-bridge write under a multiplexed secondary profile's scope, and instead return the YAML values as a dict merged into this profile's own PlatformConfig.extra. - Make require_mention/free_response_channels read extra first (matching the existing allowed_channels precedent), falling back to _get_scoped_secret() instead of raw os.getenv when extra is absent -- fixing a residual gap the DingTalk fix (#100615, this series' item 6) left in its own analogous extra-first-with-raw-fallback read sites (_dingtalk_require_mention et al. still fall back to bare os.getenv). - Switch __init__'s url/reply_mode, validate_mattermost_config's url, and _standalone_send's url to _get_scoped_secret(). - Leave check_mattermost_requirements() (no longer reads any MATTERMOST_* var on current main -- just an aiohttp-importability probe) and _is_connected() (already scope-aware via hermes_cli.gateway.get_env_value, which itself routes through agent.secret_scope.get_secret) untouched. Adds a new TestMultiplexProfileScope class to tests/gateway/test_mattermost.py (7 tests) mirroring the fixture/assertion style established in tests/gateway/test_line_plugin.py's TestMultiplexProfileScope, plus two tests exercising _apply_yaml_config's new seeded-dict return directly. Mutation-verified: stashed the production fix and confirmed 5 of 7 new tests fail against pre-fix code (the other 2 are non-differentiating regression guards -- extra-wins-over-env and unscoped-default-profile- precedence -- which correctly pass either way). Restored the fix; all 30 tests in the file, the plugin-setup test, and the full 75-test tests/gateway/test_adapter_startup_secret_scope.py suite pass.