|
| 1 | +# AGENTS.md |
| 2 | + |
| 3 | +Guidance for AI coding agents working on this repository. Human contributors should read |
| 4 | +[CONTRIBUTING.md](CONTRIBUTING.md) and [README.md](README.md) first — this file is the condensed, |
| 5 | +machine-facing version. |
| 6 | + |
| 7 | +## Project overview |
| 8 | + |
| 9 | +SafeChat Slack Bot is a Slack bot (Slack Bolt, Socket Mode) that detects PII — Brazilian CPF, email |
| 10 | +addresses and Brazilian phone numbers — in channel messages and replies in-thread asking the author |
| 11 | +not to share sensitive data. Python 3.11, Poetry, Docker-first, i18n via gettext. |
| 12 | + |
| 13 | +## Repo layout |
| 14 | + |
| 15 | +``` |
| 16 | +src/bot.py entrypoint: builds AsyncApp, starts AsyncSocketModeHandler |
| 17 | +src/listeners/register.py registers every listener |
| 18 | +src/listeners/messages/regex_message.py new messages matching the compiled pattern |
| 19 | +src/listeners/messages/message_changed.py edited messages (subtype message_changed) |
| 20 | +src/rules/constants.py the regex patterns |
| 21 | +src/rules/pattern.py Pattern singleton: compiles the rules, find_all(text) -> int |
| 22 | +src/config/settings.py settings.conf + ENV via ConfigParser |
| 23 | +src/config/language.py gettext wrapper: language.translate(msgid) |
| 24 | +src/locales/{en,pt_BR}/LC_MESSAGES/base.po translations |
| 25 | +tests/ mirrors src/ |
| 26 | +``` |
| 27 | + |
| 28 | +## Setup |
| 29 | + |
| 30 | +Prerequisites: Python 3.11, Docker + Docker Compose, `gettext` (provides `msgfmt`), Poetry. |
| 31 | + |
| 32 | +- `make docker/install` — recommended, and what CI runs. |
| 33 | +- `make local/install` — local Poetry install. |
| 34 | + |
| 35 | +Both targets create `.env` from [env.template](env.template) if absent and compile the `.mo` files. |
| 36 | +`.env` and `*.mo` are gitignored and must stay that way — never commit either. |
| 37 | + |
| 38 | +## Commands |
| 39 | + |
| 40 | +| Task | Docker (canonical) | Local | |
| 41 | +| --- | --- | --- | |
| 42 | +| install | `make docker/install` | `make local/install` | |
| 43 | +| tests | `make docker/test` | `make local/tests` | |
| 44 | +| lint | `make docker/lint` | `make local/lint` | |
| 45 | +| lint + autofix | `make docker/lint/fix` | `make local/lint/fix` | |
| 46 | +| run | `make docker/run` | `make local/run` | |
| 47 | +| compile translations | `make generate-mo-files` | `make generate-mo-files` | |
| 48 | + |
| 49 | +CI ([`.github/workflows/pull_request.yml`](.github/workflows/pull_request.yml)) runs |
| 50 | +`make docker/install` → `make docker/lint` → `make docker/test`. See the [Makefile](Makefile) for |
| 51 | +every target. |
| 52 | + |
| 53 | +## Code style |
| 54 | + |
| 55 | +Ruff, configured in [pyproject.toml](pyproject.toml): `line-length = 120`, `target-version = py311`, |
| 56 | +4-space indent, double quotes. Lint rules: `E`, `F`, `W` (pycodestyle/pyflakes), `I` (isort), |
| 57 | +`N` (pep8-naming), `S` (flake8-bandit). Run `make local/lint/fix` before committing. |
| 58 | + |
| 59 | +Project ethos from [CONTRIBUTING.md](CONTRIBUTING.md): be pythonic, DRY, KISS. |
| 60 | + |
| 61 | +## Testing |
| 62 | + |
| 63 | +`pytest` with `testpaths = ["tests"]` and `pythonpath = ["src"]`. |
| 64 | + |
| 65 | +- Tests in this repo are `unittest.TestCase` / `IsolatedAsyncioTestCase` classes with |
| 66 | + `unittest.mock` (`AsyncMock`, `MagicMock`, `patch`) — **not** bare pytest functions. Follow the |
| 67 | + existing style. |
| 68 | +- Name tests for the behaviour they assert, e.g. `test_if_text_can_be_a_cpf_with_success`. |
| 69 | +- Coverage runs in branch mode with **`fail_under = 100`** (`src/bot.py` omitted). New code without |
| 70 | + tests breaks the build. |
| 71 | +- `pytest-asyncio` is installed but no `asyncio_mode` is configured — write async tests with |
| 72 | + `IsolatedAsyncioTestCase`. |
| 73 | + |
| 74 | +## Adding a detection rule |
| 75 | + |
| 76 | +1. Add the regex to `src/rules/constants.py`. |
| 77 | +2. Append it to `self.rules` in `Pattern.__init__` (`src/rules/pattern.py`). |
| 78 | +3. Add positive **and** negative cases to `tests/rules/test_pattern.py`. |
| 79 | + |
| 80 | +Watch for over-matching: the rules are joined with `|` into one pattern, and the current CPF regex |
| 81 | +matches any run of 11 digits — a phone number counts as a CPF. Assert exact `find_all` counts. |
| 82 | + |
| 83 | +## Internationalization |
| 84 | + |
| 85 | +User-facing strings must go through `language.translate("...")`. Add the msgid to **both** |
| 86 | +`src/locales/en/LC_MESSAGES/base.po` and `src/locales/pt_BR/LC_MESSAGES/base.po`, then run |
| 87 | +`make generate-mo-files`. Adding a new locale also requires a `msgfmt` line in the |
| 88 | +[Dockerfile](Dockerfile). |
| 89 | + |
| 90 | +## Commits and pull requests |
| 91 | + |
| 92 | +- Use [Conventional Commits](https://www.conventionalcommits.org/): `feat:`, `fix:`, `docs:`, |
| 93 | + `chore:`, `refactor:`, `test:`. (History predates this convention and is inconsistent — follow the |
| 94 | + convention going forward.) |
| 95 | +- **Never add `Co-Authored-By` lines or any AI / "Generated with" attribution** to commits or PR |
| 96 | + bodies. |
| 97 | +- Never commit directly to `main` — always work on a branch. |
| 98 | +- An issue must exist before a PR (see the [pull request template](.github/PULL_REQUEST_TEMPLATE)). |
| 99 | + Reference it with `closes #NN`. |
| 100 | +- Make sure lint and tests pass locally before opening the PR. |
| 101 | + |
| 102 | +## Security |
| 103 | + |
| 104 | +This bot handles credentials and PII by definition. Treat these as hard rules: |
| 105 | + |
| 106 | +- `SLACK_BOT_TOKEN` and `SLACK_APP_TOKEN` come from environment variables only. Never hardcode a |
| 107 | + token, never log one, and never paste a real value into docs, tests or fixtures — reference |
| 108 | + [env.template](env.template) and the variable names instead. |
| 109 | +- **Never log raw message text or matched PII.** Listeners log exceptions only; keep it that way. |
| 110 | +- Do not suppress Ruff `S` (bandit) findings with `# noqa` without a written justification. |
| 111 | +- Changes to scopes in [manifest.json](manifest.json) are security-relevant — call them out |
| 112 | + explicitly in the pull request. |
| 113 | +- Report vulnerabilities through [SECURITY.md](SECURITY.md), not a public issue. |
0 commit comments