Skip to content

Commit b8295f3

Browse files
chore: improve the AGENTS.md
1 parent 72a90d2 commit b8295f3

File tree

1 file changed

+107
-56
lines changed

1 file changed

+107
-56
lines changed

AGENTS.md

Lines changed: 107 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@ Slack Bolt for Python -- a framework for building Slack apps in Python.
1616

1717
## Environment Setup
1818

19+
You can verify the venv is active by checking `echo $VIRTUAL_ENV`. If tools like `black`, `flake8`, `mypy` or `pytest` are not found, ask the user to activate the venv.
20+
1921
A python virtual environment (`venv`) should be activated before running any commands.
2022

2123
```bash
@@ -29,18 +31,30 @@ source .venv/bin/activate
2931
./scripts/install.sh
3032
```
3133

32-
You can verify the venv is active by checking `echo $VIRTUAL_ENV`. If tools like `black`, `flake8`, `mypy` or `pytest` are not found, ask the user to activate the venv.
33-
3434
## Common Commands
3535

36-
### Testing
36+
### Pre-submission Checklist
3737

38-
Always use the project scripts instead of calling `pytest` directly:
38+
Before considering any work complete, you MUST run these commands in order and confirm they all pass:
39+
40+
```bash
41+
./scripts/format.sh --no-install # 1. Format
42+
./scripts/lint.sh --no-install # 2. Lint
43+
./scripts/run_tests.sh <relevant> # 3. Run relevant tests (see Testing below)
44+
./scripts/run_mypy.sh --no-install # 4. Type check
45+
```
46+
47+
To run everything at once (installs deps + formats + lints + tests + typechecks):
3948

4049
```bash
41-
# Install all dependencies and run all tests (formats, lints, tests, typechecks)
4250
./scripts/install_all_and_run_tests.sh
51+
```
4352

53+
### Testing
54+
55+
Always use the project scripts instead of calling `pytest` directly:
56+
57+
```bash
4458
# Run a single test file
4559
./scripts/run_tests.sh tests/scenario_tests/test_app.py
4660

@@ -51,16 +65,77 @@ Always use the project scripts instead of calling `pytest` directly:
5165
### Formatting, Linting, Type Checking
5266

5367
```bash
54-
# Format (black, line-length=125)
68+
# Format -- Black, configured in pyproject.toml
5569
./scripts/format.sh --no-install
5670

57-
# Lint (flake8, line-length=125, ignores: F841,F821,W503,E402)
71+
# Lint -- Flake8, configured in .flake8
5872
./scripts/lint.sh --no-install
5973

60-
# Type check (mypy)
74+
# Type check -- mypy, configured in pyproject.toml
6175
./scripts/run_mypy.sh --no-install
6276
```
6377

78+
## Critical Conventions
79+
80+
### Sync/Async Mirroring Rule
81+
82+
**When modifying any sync module, you MUST also update the corresponding async module (and vice versa).** This is the most important convention in this codebase.
83+
84+
Almost every module has both a sync and async variant. Async files use the `async_` prefix alongside their sync counterpart:
85+
86+
```text
87+
slack_bolt/middleware/custom_middleware.py # sync
88+
slack_bolt/middleware/async_custom_middleware.py # async
89+
90+
slack_bolt/context/say/say.py # sync
91+
slack_bolt/context/say/async_say.py # async
92+
93+
slack_bolt/listener/custom_listener.py # sync
94+
slack_bolt/listener/async_listener.py # async
95+
```
96+
97+
**Modules that come in sync/async pairs:**
98+
99+
- `slack_bolt/app/` -- `app.py` / `async_app.py`
100+
- `slack_bolt/middleware/` -- every middleware has an `async_` counterpart
101+
- `slack_bolt/listener/` -- `listener.py` / `async_listener.py`, plus error/completion/start handlers
102+
- `slack_bolt/listener_matcher/` -- `builtins.py` / `async_builtins.py`
103+
- `slack_bolt/context/` -- each subdirectory (e.g., `say/`, `ack/`, `respond/`) has `async_` variants
104+
- `slack_bolt/kwargs_injection/` -- `args.py` / `async_args.py`, `utils.py` / `async_utils.py`
105+
106+
**Adapters are an exception:** Most adapters are sync-only or async-only depending on the framework. Async-native frameworks (FastAPI, Starlette, Sanic, Tornado, ASGI, Socket Mode) have `async_handler.py`. Sync-only frameworks (Flask, Django, Bottle, CherryPy, Falcon, Pyramid, AWS Lambda, Google Cloud Functions, WSGI) have `handler.py`.
107+
108+
### Prefer the Middleware Pattern
109+
110+
Middleware is the project's preferred approach for cross-cutting concerns. Before adding logic to individual listeners or utility functions, consider whether it belongs in the middleware chain.
111+
112+
**When to use middleware:**
113+
114+
- Cross-cutting concerns that apply to many or all requests (logging, metrics, observability)
115+
- Request validation, transformation, or enrichment
116+
- Authorization extensions beyond the built-in `SingleTeamAuthorization`/`MultiTeamsAuthorization`
117+
- Feature-level request handling (the `Assistant` middleware in `slack_bolt/middleware/assistant/assistant.py` is the canonical example -- it intercepts assistant thread events and dispatches them to registered sub-listeners)
118+
119+
**How to implement middleware:**
120+
121+
1. Subclass `Middleware` (sync) and implement `process(self, *, req, resp, next)`. Call `next()` to continue the chain.
122+
2. Subclass `AsyncMiddleware` (async) and implement `async_process(self, *, req, resp, next)`. Call `await next()` to continue.
123+
3. Export from `slack_bolt/middleware/__init__.py` (sync) and `slack_bolt/middleware/async_builtins.py` (async).
124+
4. Register via `App(middleware=[...])` or the `@app.middleware` decorator.
125+
126+
**Simple example using the decorator:**
127+
128+
```python
129+
@app.middleware
130+
def log_request(logger, body, next):
131+
logger.debug(f"Incoming request: {body.get('type')}")
132+
return next()
133+
```
134+
135+
### Single Runtime Dependency Rule
136+
137+
The core package depends ONLY on `slack_sdk` (defined in `pyproject.toml`). Never add runtime dependencies to `pyproject.toml`. Additional dependencies go in the appropriate `requirements/*.txt` file.
138+
64139
## Architecture
65140

66141
### Request Processing Pipeline
@@ -90,49 +165,12 @@ Listeners receive arguments by parameter name. The framework inspects function s
90165

91166
Each adapter in `slack_bolt/adapter/` converts between a web framework's request/response types and `BoltRequest`/`BoltResponse`. Adapters exist for: Flask, FastAPI, Django, Starlette, Sanic, Bottle, Tornado, CherryPy, Falcon, Pyramid, AWS Lambda, Google Cloud Functions, Socket Mode, WSGI, ASGI, and more.
92167

93-
### Sync/Async Mirroring Pattern
94-
95-
**This is the most important pattern in this codebase.** Almost every module has both a sync and async variant. When you modify one, you almost always must modify the other.
96-
97-
**File naming convention:** Async files use the `async_` prefix alongside their sync counterpart:
98-
99-
```text
100-
slack_bolt/middleware/custom_middleware.py # sync
101-
slack_bolt/middleware/async_custom_middleware.py # async
102-
103-
slack_bolt/context/say/say.py # sync
104-
slack_bolt/context/say/async_say.py # async
105-
106-
slack_bolt/listener/custom_listener.py # sync
107-
slack_bolt/listener/async_listener.py # async
108-
109-
slack_bolt/adapter/fastapi/async_handler.py # async-only (no sync FastAPI adapter)
110-
slack_bolt/adapter/flask/handler.py # sync-only (no async Flask adapter)
111-
```
112-
113-
**Which modules come in sync/async pairs:**
114-
115-
- `slack_bolt/app/` -- `app.py` / `async_app.py`
116-
- `slack_bolt/middleware/` -- every middleware has an `async_` counterpart
117-
- `slack_bolt/listener/` -- `listener.py` / `async_listener.py`, plus error/completion/start handlers
118-
- `slack_bolt/listener_matcher/` -- `builtins.py` / `async_builtins.py`
119-
- `slack_bolt/context/` -- each subdirectory (e.g., `say/`, `ack/`, `respond/`) has `async_` variants
120-
- `slack_bolt/kwargs_injection/` -- `args.py` / `async_args.py`, `utils.py` / `async_utils.py`
121-
122-
**Adapters are an exception:** Most adapters are sync-only or async-only depending on the framework. Async-native frameworks (FastAPI, Starlette, Sanic, Tornado, ASGI, Socket Mode) have `async_handler.py`. Sync-only frameworks (Flask, Django, Bottle, CherryPy, Falcon, Pyramid, AWS Lambda, Google Cloud Functions, WSGI) have `handler.py`.
123-
124168
### AI Agents & Assistants
125169

126170
`BoltAgent` (`slack_bolt/agent/`) provides `chat_stream()`, `set_status()`, and `set_suggested_prompts()` for AI-powered agents. `Assistant` middleware (`slack_bolt/middleware/assistant/`) handles assistant thread events.
127171

128172
## Key Development Patterns
129173

130-
### Adding or Modifying Middleware
131-
132-
1. Implement the sync version in `slack_bolt/middleware/` (subclass `Middleware`, implement `process()`)
133-
2. Implement the async version with `async_` prefix (subclass `AsyncMiddleware`, implement `async_process()`)
134-
3. Export built-in middleware from `slack_bolt/middleware/__init__.py` (sync) and `async_builtins.py` (async)
135-
136174
### Adding a Context Utility
137175

138176
Each context utility lives in its own subdirectory under `slack_bolt/context/`:
@@ -153,14 +191,21 @@ Then wire it into `BoltContext` (`slack_bolt/context/context.py`) and `AsyncBolt
153191
2. Add `__init__.py` and `handler.py` (or `async_handler.py` for async frameworks)
154192
3. The handler converts the framework's request to `BoltRequest`, calls `app.dispatch()`, and converts `BoltResponse` back
155193
4. Add the framework to `requirements/adapter.txt` with version constraints
156-
5. Add adapter tests in `tests/adapter_tests/` (or `tests/adapter_tests_async/`)
194+
5. Add adapter tests in `tests/adapter_tests/` (sync) or `tests/adapter_tests_async/` (async)
157195

158196
### Adding a Kwargs-Injectable Argument
159197

160198
1. Add the new arg to `slack_bolt/kwargs_injection/args.py` and `async_args.py`
161199
2. Update the `Args` class with the new property
162200
3. Populate the arg in the appropriate context or listener setup code
163201

202+
## Security Considerations
203+
204+
- **Request Verification:** The built-in `RequestVerification` middleware validates `x-slack-signature` and `x-slack-request-timestamp` on every incoming HTTP request. Never disable this in production. It is automatically skipped for `socket_mode` requests.
205+
- **Tokens & Secrets:** `SLACK_SIGNING_SECRET` and `SLACK_BOT_TOKEN` must come from environment variables. Never hardcode or commit secrets.
206+
- **Authorization Middleware:** `SingleTeamAuthorization` and `MultiTeamsAuthorization` verify tokens and inject an authorized `WebClient` into the context. Do not bypass these.
207+
- **Tests:** Always use mock servers (`tests/mock_web_api_server/`) and dummy values. Never use real tokens in tests.
208+
164209
## Dependencies
165210

166211
The core package has a **single required runtime dependency**: `slack_sdk` (defined in `pyproject.toml`). Do not add runtime dependencies.
@@ -176,7 +221,9 @@ The core package has a **single required runtime dependency**: `slack_sdk` (defi
176221

177222
When adding a new dependency: add it to the appropriate `requirements/*.txt` file with version constraints, never to `pyproject.toml` `dependencies` (unless it's a core runtime dep, which is very rare).
178223

179-
## Test Organization
224+
## Test Organization and CI
225+
226+
### Directory Structure
180227

181228
- `tests/scenario_tests/` -- Integration-style tests with realistic Slack payloads
182229
- `tests/slack_bolt/` -- Unit tests mirroring the source structure
@@ -188,15 +235,19 @@ When adding a new dependency: add it to the appropriate `requirements/*.txt` fil
188235

189236
**Mock server:** Many tests use `tests/mock_web_api_server/` to simulate Slack API responses. Look at existing tests for usage patterns rather than making real API calls.
190237

191-
## Code Style
238+
### CI Pipeline
239+
240+
GitHub Actions (`.github/workflows/ci-build.yml`) runs on every push to `main` and every PR:
192241

193-
- **Black** formatter configured in `pyproject.toml` (line-length=125)
194-
- **Flake8** linter configured in `.flake8` (line-length=125, ignores: F841,F821,W503,E402)
195-
- **MyPy** configured in `pyproject.toml`
196-
- **pytest** configured in `pyproject.toml`
242+
- **Lint** -- `./scripts/lint.sh` on latest Python
243+
- **Typecheck** -- `./scripts/run_mypy.sh` on latest Python
244+
- **Unit tests** -- full test suite across Python 3.7--3.14 matrix
245+
- **Code coverage** -- uploaded to Codecov
197246

198-
## GitHub & CI/CD
247+
## PR and Commit Guidelines
199248

200-
- `.github/` -- GitHub-specific configuration and documentation
201-
- `.github/workflows/` -- Continuous integration pipeline definitions that run on GitHub Actions
202-
- `.github/maintainers_guide.md` -- Maintainer workflows and release process
249+
- PRs target the `main` branch
250+
- You MUST run `./scripts/install_all_and_run_tests.sh` before submitting
251+
- PR template (`.github/pull_request_template.md`) requires: Summary, Testing steps, Category checkboxes (`App`, `AsyncApp`, Adapters, Docs, Others)
252+
- Requirements: CLA signed, test suite passes, code review approval
253+
- Commits should be atomic with descriptive messages. Reference related issue numbers.

0 commit comments

Comments
 (0)