Skip to content

refactor(slack): extract allow-list source behind AllowListSource trait#1152

Merged
thepagent merged 1 commit into
openabdev:mainfrom
antigenius0910:feature/issue-1109-allow-list-source-trait
Jun 19, 2026
Merged

refactor(slack): extract allow-list source behind AllowListSource trait#1152
thepagent merged 1 commit into
openabdev:mainfrom
antigenius0910:feature/issue-1109-allow-list-source-trait

Conversation

@antigenius0910

@antigenius0910 antigenius0910 commented Jun 19, 2026

Copy link
Copy Markdown
Contributor

Summary

  • Extracts the Slack allow-list source into a small AllowListSource trait (src/allow_list.rs)
  • In-tree default StaticAllowList wraps [slack].allowed_usersbehaviour-preserving refactor
  • src/slack.rs::run_slack_adapter takes Arc<dyn AllowListSource> instead of bare HashSet<String>
  • Each dispatch captures a snapshot via .allowed_users()Arc<HashSet<String>>, lock-free on the read path
  • Downstream forks can plug in alternative implementations (file-watching, IdP sync, etc.) without modifying gate-check call sites

Context

This PR supersedes #1109 (sync-feature for [slack].allowed_users in core). Per Discord discussion on 2026-06-16, the consensus is to keep openab core IdP-agnostic and provide a clean extension point so downstream forks can add IdP-aware sources themselves.

@pahud-nodejs approved the trait-only refactor direction: 「完全合理,來吧!這很好!」

The actual IdP-sync implementation (e.g. Okta Groups → file → hot-reload) lives in downstream forks; openab core only commits to the trait shape.

File changes

File Change
src/allow_list.rs (new) Trait + StaticAllowList default + 3 unit tests (round-trip, snapshot Arc-sharing, hot-swap through dyn dispatch via a mock impl)
src/main.rs mod allow_list;; wrap config-loaded HashSet in StaticAllowList before passing to run_slack_adapter
src/slack.rs run_slack_adapter parameter type change; use crate::allow_list::AllowListSource;; two spawn-site preambles resolve .allowed_users() to a snapshot before dispatch

Net: ~14 lines changed in existing files + ~110 lines of new module (mostly tests). No behaviour change.

Scope

  • Slack only. discord and gateway adapters continue to take bare HashSet<String> and may adopt the trait in follow-ups if their communities want it.
  • No new config fields. TOML schema unchanged.
  • No new dependencies.

Test plan

  • cargo check — clean
  • cargo test --bin openab allow_list — 3/3 passing
  • cargo test --bin openab — 512 passing; one pre-existing failure (secrets::tests::resolve_exec_nonzero_exit) also fails on clean upstream/main, unrelated to this PR
  • cargo clippy --bin openab -- -D warnings — clean
  • cargo fmt --check — formatting diff reported is on a pre-existing line (socket_idle test, src/slack.rs:~2332) that also reproduces on clean upstream/main; not introduced here

Out of scope (follow-ups)

  • File-watching / IdP-sync implementation of AllowListSource — lives in downstream forks
  • Same trait pattern for [discord].allowed_users / [gateway].allowed_users
  • Same pattern for allowed_channels / trusted_bot_ids

Closes #1109 (superseded by this trait-only direction)

Introduces a small `AllowListSource` trait (src/allow_list.rs) that the
Slack adapter consumes via `Arc<dyn AllowListSource>` instead of a bare
`HashSet<String>`. The in-tree `StaticAllowList` default wraps a fixed
set loaded once from `[slack].allowed_users`, preserving existing
behaviour exactly.

The seam lets downstream forks plug in alternative implementations
(e.g. a file-watching impl that mirrors an IdP group) without modifying
the gate-check call sites. Snapshot semantics (each dispatch captures
`Arc<HashSet<String>>` via `.allowed_users()`) make hot-reload
implementations lock-free on the read path.

Scope is Slack-only; discord/gateway adapters continue to use their
existing `HashSet<String>` parameters and may adopt the trait in
follow-ups if their communities want it.

Per Discord discussion on RFC openabdev#1109 — the original sync-feature
direction in openabdev#1109 is superseded by this trait-only refactor; IdP-sync
implementations live in downstream forks.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@openab-app openab-app Bot added the closing-soon PR missing Discord Discussion URL — will auto-close in 24 hours. label Jun 19, 2026
@chaodu-agent

Copy link
Copy Markdown
Collaborator

LGTM ✅ — Clean trait extraction with zero behaviour change; well-tested and narrowly scoped.

What This PR Does

Extracts the Slack allow-list gate into a pluggable AllowListSource trait so downstream forks can inject alternative implementations (file-watching, IdP sync) without modifying core dispatch logic.

How It Works

  • New src/allow_list.rs defines the AllowListSource trait returning Arc<HashSet<String>> snapshots (lock-free on the read path).
  • StaticAllowList is the in-tree default wrapping the config-loaded set — semantically identical to the previous HashSet<String> parameter.
  • run_slack_adapter now takes Arc<dyn AllowListSource> and each spawn site calls .allowed_users() to capture a snapshot before dispatch, exactly mirroring the old .clone() behaviour.
  • handle_message still takes &HashSet<String> — minimal blast radius.

Findings

# Severity Finding Location
1 🟢 Trait design is idiomatic: Send + Sync bound, Arc snapshot pattern, and SwappableSource test proving the seam works through dyn dispatch. src/allow_list.rs
2 🟢 Only 14 net lines changed in existing files — very low risk for a refactor of this nature. src/main.rs, src/slack.rs
3 🟢 Scope is appropriately limited to Slack only; Discord/Gateway adapters are left untouched per plan.
What's Good (🟢)
  • Trait shape is minimal and correct — single method returning Arc<HashSet<String>> is the right abstraction for a read-heavy, occasionally-swapped set.
  • Test coverage is thorough: round-trip, Arc pointer identity (proving zero-copy on static path), and a mock hot-swap proving the extension point works.
  • PR body is exemplary: clear context, file-by-file changelog, explicit scope boundaries, and test plan with pre-existing failures called out.
  • No new dependencies added.
Baseline Check
  • PR opened: 2026-06-19
  • Main already has: allowed_users: HashSet<String> passed directly to run_slack_adapter and cloned at each spawn site.
  • Net-new value: The trait seam (AllowListSource) enabling downstream hot-reload implementations without forking core dispatch logic.
CI Note

The failing check job is the "Discord Discussion URL" metadata gate — not a code/build failure. All smoke tests are still pending at review time.

@openab-app openab-app Bot removed the closing-soon PR missing Discord Discussion URL — will auto-close in 24 hours. label Jun 19, 2026
@thepagent thepagent merged commit 0a24940 into openabdev:main Jun 19, 2026
19 of 21 checks passed
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.

RFC: Optional synced source for Slack allowed_users

3 participants