The plugin surface is too small for the plugins that exist: every official plugin imports past postulo.plugins.api #229

Closed
opened 2026-09-15 21:23:27 +00:00 by tiagoagueda · 0 comments
Owner

postulo/plugins/api.py:25-28 says anything outside the surface "may move without warning". Every official Python plugin imports past it anyway, and the wiki teaches those imports. Found in the 2026-09-15 code audit, which read the sibling repositories.

What the plugins reach for

  • postulo-apprise, postulo-paperless: postulo.plugins.http (DestinationRefused, check_destination) and postulo.plugins.base.
  • postulo-helloworld: postulo.plugins.base.
  • postulo-dav:
    • postulo.applications.ical, .models, .services;
    • postulo.core.phone_numbers, web_links;
    • postulo.jobs.models.Contact;
    • postulo.plugins.models.SyncLink.
  • postulo-imap: Application, Suggestion, suggest, Contact.
  • Wiki: Writing a plugin (lines ~239, ~344, ~440, ~481) shows from postulo.plugins.base import …, from postulo.plugins import http and from postulo.notifications.base import Notification.

What is missing from the surface

  • The notifier contract. Notification, NotifierPlugin and EVENTS live in notifications/base.py and are not on api (the core's own test_plugin_surface.py records the built-in reaching past for them). Notification also lacks:
    • a stable key, so retries cannot be deduplicated;
    • language;
    • occurred_at;
    • structured data (the reminder, application or due time).
  • Outbound checks for non-client code: DestinationRefused, check_destination, plus the guarded-socket helper from the outbound-requests issue (#215).
  • Sync support: SyncLink and suggest, which the wiki already treats as public.
  • A small sync facade for contacts and interviews: get_or_create_company, reschedule_interview, settle_interview, record_event, primary phone and link getters, and ical.event_lines.

Proposal

`postulo/plugins/api.py:25-28` says anything outside the surface "may move without warning". Every official Python plugin imports past it anyway, and the wiki teaches those imports. Found in the 2026-09-15 code audit, which read the sibling repositories. ## What the plugins reach for - **postulo-apprise, postulo-paperless:** `postulo.plugins.http` (`DestinationRefused`, `check_destination`) and `postulo.plugins.base`. - **postulo-helloworld:** `postulo.plugins.base`. - **postulo-dav:** - `postulo.applications.ical`, `.models`, `.services`; - `postulo.core.phone_numbers`, `web_links`; - `postulo.jobs.models.Contact`; - `postulo.plugins.models.SyncLink`. - **postulo-imap:** `Application`, `Suggestion`, `suggest`, `Contact`. - **Wiki:** *Writing a plugin* (lines ~239, ~344, ~440, ~481) shows `from postulo.plugins.base import …`, `from postulo.plugins import http` and `from postulo.notifications.base import Notification`. ## What is missing from the surface - **The notifier contract.** `Notification`, `NotifierPlugin` and `EVENTS` live in `notifications/base.py` and are not on `api` (the core's own `test_plugin_surface.py` records the built-in reaching past for them). `Notification` also lacks: - a stable `key`, so retries cannot be deduplicated; - `language`; - `occurred_at`; - structured `data` (the reminder, application or due time). - **Outbound checks for non-client code:** `DestinationRefused`, `check_destination`, plus the guarded-socket helper from the outbound-requests issue (#215). - **Sync support:** `SyncLink` and `suggest`, which the wiki already treats as public. - **A small sync facade** for contacts and interviews: `get_or_create_company`, `reschedule_interview`, `settle_interview`, `record_event`, primary phone and link getters, and `ical.event_lines`. ## Proposal - Add these names to `postulo.plugins.api`, each with a wiki line (`test_wiki_surface.py` will require it). - Extend `Notification` with `key`, `language`, `occurred_at` and `data`, and document them. - Fix every wiki example to import only from `postulo.plugins.api`. - Publish the AST check from `tests/test_plugin_surface.py` as a reusable test (for example in `postulo-messages`, or a `postulo.plugins.testing` module) so each plugin's CI can run it. - Then move each official plugin onto the surface; that work is tracked in postulo/postulo-imap#1, postulo/postulo-apprise#1, postulo/postulo-dav#2, postulo/postulo-paperless#2, postulo/postulo-helloworld#1, postulo/postulo-mcp#3.
tiagoagueda added this to the 0.4.0 milestone 2026-09-15 21:33:26 +00:00
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
Postulo/postulo#229
No description provided.