Skip to content

feat(core): add a resource registry beside the tool catalog [TOO-1932] - #918

Open
EricGustin wants to merge 1 commit into
ericgustin/too-1931-resource-schema-to-arcade-corefrom
ericgustin/too-1932-resource-registry
Open

feat(core): add a resource registry beside the tool catalog [TOO-1932]#918
EricGustin wants to merge 1 commit into
ericgustin/too-1931-resource-schema-to-arcade-corefrom
ericgustin/too-1932-resource-registry

Conversation

@EricGustin

@EricGustin EricGustin commented Aug 25, 2026

Copy link
Copy Markdown
Member

Summary

Adds a URI-to-resource registry to arcade-core, so a worker has somewhere to keep the resources a toolkit ships. Its own type, its own file.

Resources on this path are static. Contents resolve once when a toolkit registers and are held from then on, so nothing executes while a request is in flight and the endpoints that read this registry stay a thin serialization layer.

Part 2 of 6. Stacked on #923.

Resolves: https://linear.app/arcadedev/issue/TOO-1932

Design decisions

The registry hangs off ToolCatalog. That is the one object the server factory hands to the worker (worker.catalog = catalog), so anything living elsewhere needs new plumbing to reach it. The registry is still a separate type in a separate file; the catalog just carries it. It is a PrivateAttr with a default_factory, so two catalogs can never end up sharing one registry. There is a test for that, because the bare-mutable-class-default version of this bug is easy to write and hard to see.

Listing is ordered by URI, and the order is kept on the write. A worker can run as several processes behind one address, and a cursor issued by one process has to mean the same thing when the next page lands on a different one. Insertion order gives you no such guarantee.

add keeps a URI index sorted with bisect.insort and list slices it, so the read is O(page_size) rather than sorting the whole set each time. A worker registers once at startup and lists on every request, so the ordering cost belongs on the write. Re-registering a URI replaces in place without inserting a second time.

page_size validates on assignment rather than only in __init__, because it is a public attribute callers set directly. A non-positive value makes cursor paging non-terminating: zero hands back an empty page and the same cursor forever, and a negative one walks the offset backwards into a cursor this registry then rejects as malformed.

Text and blob stay separate types. Keeping them apart by type, instead of by which field happens to be empty, means a zero-length document and a zero-length blob remain distinguishable all the way out to the caller.

Test plan

  • 14 new tests in libs/tests/core/test_resource_registry.py: the text/blob split, cursor round-tripping, rejection of a cursor this registry did not issue, per-catalog isolation, that replacing a URI leaves exactly one entry in the ordered index, and that a non-positive page_size is refused at construction and at assignment
  • uv run pytest libs/tests: 3807 passed, 1 skipped
  • uv run mypy . clean in each of the four libraries
  • ruff check and ruff format --check clean on every touched file

Author checklist

Before moving this PR from Draft to Ready for Review:

  • Linked to a Linear ticket or GitHub issue (above)
  • I understand every change in the diff
  • Runs locally, exercised through the end-user path (not just unit tests)
  • make check and make test are green locally; CI is expected to pass
  • I've pulled the branch fresh and reviewed my own diff top-to-bottom
  • I'd merge it myself if a teammate said LGTM right now

Note

Low Risk
Adds new in-memory registry APIs and catalog wiring only; no request-path execution or changes to auth or tool execution.

Overview
Introduces a new ResourceRegistry in arcade_core.resources so workers can hold static toolkit resources (for upcoming resources/* serving) with contents resolved once at registration, not on each request.

The registry maps URIs to RegisteredResource entries, wrapping str as TextResourceContents and bytes as base64 BlobResourceContents so empty text and empty blobs stay distinct. add replaces by URI without duplicating the sorted URI index; list pages with opaque offset cursors and URI sort order (maintained via insort on write) so pagination stays consistent across multi-process workers. page_size is validated on assignment; malformed cursors raise InvalidCursorError.

ToolCatalog now exposes a per-instance registry via PrivateAttr(default_factory=ResourceRegistry) and a resources property—the catalog is the delivery path to the worker without merging resource types into tool catalog logic. arcade-core bumps to 4.13.0; test_resource_registry.py adds coverage for registration, paging, cursors, and catalog isolation.

Reviewed by Cursor Bugbot for commit 0c3d732. Bugbot is set up for automated code reviews on this repo. Configure here.

@codecov

codecov Bot commented Aug 25, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 93.75000% with 5 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
libs/arcade-core/arcade_core/resources.py 93.24% 5 Missing ⚠️
Files with missing lines Coverage Δ
libs/arcade-core/arcade_core/catalog.py 91.26% <100.00%> (+0.07%) ⬆️
libs/arcade-core/arcade_core/resources.py 93.24% <93.24%> (ø)
🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@EricGustin
EricGustin force-pushed the ericgustin/too-1931-mcp-resource-models-to-arcade-core branch from b5642b7 to 1d296c5 Compare August 26, 2026 06:56
@EricGustin
EricGustin force-pushed the ericgustin/too-1932-resource-registry branch from 28189ab to 155d9b0 Compare August 26, 2026 06:56
Comment thread libs/arcade-core/arcade_core/resources.py Outdated
@EricGustin
EricGustin force-pushed the ericgustin/too-1932-resource-registry branch from 155d9b0 to 32e510b Compare August 26, 2026 18:20
@EricGustin
EricGustin force-pushed the ericgustin/too-1931-resource-schema-to-arcade-core branch from 1d296c5 to d3eb3a2 Compare August 26, 2026 20:32
@EricGustin
EricGustin force-pushed the ericgustin/too-1932-resource-registry branch from 32e510b to 946771a Compare August 26, 2026 20:32
@EricGustin
EricGustin marked this pull request as ready for review August 26, 2026 21:19
@greptile-apps

greptile-apps Bot commented Aug 26, 2026

Copy link
Copy Markdown

Greptile Summary

The PR adds a static URI-to-resource registry to arcade-core and exposes an isolated registry through each ToolCatalog.

  • Stores text and binary resource contents as distinct schema types.
  • Provides deterministic URI-ordered cursor pagination and replacement by URI.
  • Validates page_size during construction and direct assignment, resolving the previously reported pagination issue.
  • Bumps arcade-core from 4.12.0 to 4.13.0 and adds registry coverage.

Confidence Score: 5/5

The PR appears safe to merge.

No blocking failure remains.

Important Files Changed

Filename Overview
libs/arcade-core/arcade_core/resources.py Adds resource storage, lookup, deterministic pagination, typed text/blob contents, cursor validation, and positive page-size enforcement; the previous pagination defect is fixed.
libs/arcade-core/arcade_core/catalog.py Attaches a separately instantiated resource registry to each ToolCatalog through a Pydantic private attribute.
libs/tests/core/test_resource_registry.py Covers resource types, cursor behavior, ordering, replacement, catalog isolation, missing resources, and page-size validation.
libs/arcade-core/pyproject.toml Applies the required minor version increase for the new core-library functionality.

Reviews (3): Last reviewed commit: "feat(core): add a resource registry besi..." | Re-trigger Greptile

Comment thread libs/arcade-core/arcade_core/resources.py
@EricGustin
EricGustin force-pushed the ericgustin/too-1931-resource-schema-to-arcade-core branch from d3eb3a2 to 77fee3a Compare August 26, 2026 22:08
@EricGustin
EricGustin force-pushed the ericgustin/too-1932-resource-registry branch from 946771a to 590e7f4 Compare August 26, 2026 22:08
@EricGustin
EricGustin force-pushed the ericgustin/too-1931-resource-schema-to-arcade-core branch from 77fee3a to b8871e0 Compare August 27, 2026 00:15
@EricGustin
EricGustin force-pushed the ericgustin/too-1932-resource-registry branch from 590e7f4 to ccc7675 Compare August 27, 2026 00:15
A URI-to-resource registry in arcade-core, with its own type and its own file.

Resources on this path are static, so contents resolve once at registration and
are held: the endpoints that read this registry stay a serialization shim with
nothing executing on the request path.

The registry hangs off ToolCatalog because that is the only object handed to
the worker (worker.catalog = catalog). Anything not on the catalog needs new
plumbing through the server factory to get there. It is a private attribute
with a default_factory rather than a bare mutable class default, so two
catalogs never share one registry.

Text and binary are kept apart by type rather than by emptiness, so a
zero-length document and a zero-length blob stay distinguishable to the caller.

Listing is ordered by URI so a cursor keeps its meaning when the next page is
served by a different process. A worker can run as several processes behind one
address, and none of them shares insertion order.

add keeps the URI index sorted so list is a slice. A worker registers its
resources once at startup and lists them on every request, so the ordering cost
belongs on the write.
@EricGustin
EricGustin force-pushed the ericgustin/too-1931-resource-schema-to-arcade-core branch from b8871e0 to 5944cbf Compare August 27, 2026 15:28
@EricGustin
EricGustin force-pushed the ericgustin/too-1932-resource-registry branch from ccc7675 to 0c3d732 Compare August 27, 2026 15:35

@greptile-apps greptile-apps Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Your trial has ended. Reactivate Greptile to resume code reviews.

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