Skip to content

feat(fulltext): provider-neutral external resolver hook (opt-in, last resort)#10

Merged
juniperbevensee merged 1 commit into
mainfrom
feature/external-fulltext-resolver
Jun 18, 2026
Merged

feat(fulltext): provider-neutral external resolver hook (opt-in, last resort)#10
juniperbevensee merged 1 commit into
mainfrom
feature/external-fulltext-resolver

Conversation

@juniperbevensee

Copy link
Copy Markdown
Collaborator

What

Adds an optional, provider-neutral extension point to matilde_fetch_fulltext. When MATILDE_FULLTEXT_RESOLVER_URL (or a resolver_url arg) is set, an external full-text resolver is consulted — but only after every legal open-access lookup (OpenAlex/Unpaywall/arXiv) has missed. A legal OA copy always wins, so a configured resolver is never hit when an open-access copy exists.

Why it's structured this way

This keeps the public, org-branded package open-access-only and provider-neutral: it ships no resolver service and names no provider. The operator supplies one out of band against a simple contract:

GET {resolver_url}/resolve?doi=<doi>  ->  {"pdf_url": "..."}

Any specific resolver — an institutional/library proxy, a licensed API, a self-hosted service — and its access/legal posture live entirely with whoever configures the URL, not with this repo.

Honesty by construction

A resolver hit sets is_oa: false and source: "external-resolver", and the tool message flags it as NOT open access — confirm you have the right to access it. The tool never claims a non-OA source is open access. Unset the variable and the behaviour never engages (default off).

Tests (TDD)

  • 4 engine tests: used-on-OA-miss, skipped-when-unset, never-consulted-when-legal-OA-exists, miss-falls-through-to-closed.
  • 2 handler tests: env forwarding, honest "not open access" message.
  • Full suite: 93 passed, 6 skipped.

🤖 Generated with Claude Code

… resort)

Adds an optional extension point to matilde_fetch_fulltext: when
MATILDE_FULLTEXT_RESOLVER_URL (or a resolver_url arg) is set, an external
full-text resolver is consulted — but ONLY after every legal open-access
lookup (OpenAlex/Unpaywall/arXiv) has missed. A legal OA copy always wins,
so a configured resolver is never hit when an open-access copy exists.

The package ships no such service and names no provider. The operator
supplies one out of band against the contract:
  GET {resolver_url}/resolve?doi=<doi> -> {"pdf_url": "..."}

Honesty by construction: a resolver hit sets is_oa=false and
source="external-resolver", and the tool message flags it as NOT open
access. The public repo stays open-access-only and provider-neutral; any
specific resolver (and its access/legal posture) lives entirely with the
operator who configures the URL. Unset => behaviour never engages.

Built test-first: 4 engine tests (used-on-miss, skipped-when-unset,
never-consulted-when-OA-exists, miss-falls-through) + 2 handler tests
(env forwarding, honest message). Full suite 93 passed, 6 skipped.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@juniperbevensee
juniperbevensee merged commit 4e5bd44 into main Jun 18, 2026
1 check passed
@juniperbevensee
juniperbevensee deleted the feature/external-fulltext-resolver branch June 18, 2026 00:43
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.

1 participant