Does not load on Postulo 0.3.0: the store contract moved to postulo.plugins.api #1

Closed
opened 2026-09-10 12:17:32 +00:00 by tiagoagueda · 0 comments
Owner

Observation

Against the core's 0.3.0 branch (postulo@a22e7a09), the plugin does not load:

ImportError: cannot import name 'ExternalRef' from 'postulo.documents.stores'

The test suite stops at collection, on StorePlugin. On an instance upgraded to 0.3.0, Postulo's registry catches the error and logs Plugin 'paperless' (store) could not be loaded. The instance keeps running and nothing more is sent to Paperless: a silent stop, visible only in the log.

Against the core that uv.lock pins (postulo@0aca8be9), all 15 tests pass. So the core moved, and nothing here noticed, because CI tests against the pin.

Cause

The core now has a declared plugin surface. postulo.plugins.api is "Everything a plugin may import from Postulo. Nothing else is a contract" (postulo#126), and its names keep working across a minor release, with a deprecation first. postulo#129 (b3b7adfa, 9 September) moved the store contract onto it. postulo.documents.stores keeps only Postulo's side of the arrangement.

What the plugin imports Where it is on the 0.3.0 core
postulo.documents.stores.ExternalRef postulo.plugins.api.ExternalRef; gone from stores
postulo.documents.stores.StorePlugin (tests) postulo.plugins.api.StorePlugin; gone from stores
postulo.documents.stores.DocumentMetadata postulo.plugins.api.DocumentMetadata; still importable from stores, by accident rather than promise
postulo.plugins.base.FieldSpec, TestResult postulo.plugins.api; base still has them, with no promise
postulo.plugins.http.client postulo.plugins.api.client
postulo.plugins.http.DestinationRefused not on the surface

What fixing it is

  • Import the contract from postulo.plugins.api and nowhere else, in store.py.
  • DestinationRefused needs a decision. It is how the core's client says an address was refused, but it is not a promised name yet. Either ask for it on the surface in the core, or keep importing it from postulo.plugins.http, knowingly and with a comment saying why.
  • Move the pin: uv lock --upgrade-package postulo, so CI tests against a core the plugin will actually run on. A pin that stays behind is exactly how a break like this goes green.

Worth being careful about

  • The tests reach further than the plugin (postulo.documents.archiving, postulo.documents.models, postulo.plugins.registry, postulo.plugins.models). That is reasonable for tests that exercise the whole path, but they will move without warning. Keep them few.
  • 0.3.0 is not released yet. Fixed before it is, the plugin works on the day people upgrade.

Classification

Bug. Does not load on Postulo 0.3.0.

## Observation Against the core's `0.3.0` branch (`postulo@a22e7a09`), **the plugin does not load**: ``` ImportError: cannot import name 'ExternalRef' from 'postulo.documents.stores' ``` The test suite stops at collection, on `StorePlugin`. On an instance upgraded to 0.3.0, Postulo's registry catches the error and logs `Plugin 'paperless' (store) could not be loaded`. The instance keeps running and nothing more is sent to Paperless: a silent stop, visible only in the log. Against the core that `uv.lock` pins (`postulo@0aca8be9`), all 15 tests pass. So the core moved, and nothing here noticed, because CI tests against the pin. ## Cause The core now has a declared plugin surface. `postulo.plugins.api` is *"Everything a plugin may import from Postulo. Nothing else is a contract"* (postulo#126), and its names keep working across a minor release, with a deprecation first. postulo#129 (`b3b7adfa`, 9 September) moved the store contract onto it. `postulo.documents.stores` keeps only Postulo's side of the arrangement. | What the plugin imports | Where it is on the 0.3.0 core | | --- | --- | | `postulo.documents.stores.ExternalRef` | `postulo.plugins.api.ExternalRef`; **gone from `stores`** | | `postulo.documents.stores.StorePlugin` (tests) | `postulo.plugins.api.StorePlugin`; **gone from `stores`** | | `postulo.documents.stores.DocumentMetadata` | `postulo.plugins.api.DocumentMetadata`; still importable from `stores`, by accident rather than promise | | `postulo.plugins.base.FieldSpec`, `TestResult` | `postulo.plugins.api`; `base` still has them, with no promise | | `postulo.plugins.http.client` | `postulo.plugins.api.client` | | `postulo.plugins.http.DestinationRefused` | **not on the surface** | ## What fixing it is - **Import the contract from `postulo.plugins.api` and nowhere else**, in `store.py`. - **`DestinationRefused` needs a decision.** It is how the core's client says an address was refused, but it is not a promised name yet. Either ask for it on the surface in the core, or keep importing it from `postulo.plugins.http`, knowingly and with a comment saying why. - **Move the pin**: `uv lock --upgrade-package postulo`, so CI tests against a core the plugin will actually run on. A pin that stays behind is exactly how a break like this goes green. ## Worth being careful about - **The tests reach further than the plugin** (`postulo.documents.archiving`, `postulo.documents.models`, `postulo.plugins.registry`, `postulo.plugins.models`). That is reasonable for tests that exercise the whole path, but they will move without warning. Keep them few. - **0.3.0 is not released yet.** Fixed before it is, the plugin works on the day people upgrade. ## Classification Bug. Does not load on Postulo 0.3.0.
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-paperless#1
No description provided.