# arXiv App and Builtin Extension

> Canonical path: `specs/extension/arxiv-app.md`
>
> Status: normative product and runtime specification.
>
> Related:
>
> - [`extension-platform.md`](./extension-platform.md)
> - [`../app/app-system.md`](../app/app-system.md)

## 1. Product model

AgentTeam exposes arXiv as the first-party public Product App `app_arxiv`. Its App
slug, Builtin Extension domain, and default Agent-visible Extension name are
all `arxiv`.

The App is connectionless. Installing it creates the Workspace App installation
and its default `service_installation_extensions` selection. It does not create
a `service_connections` row, request credentials, or show a provider
configuration form. The upgrade migration and fresh bootstrap register only
global Marketplace metadata; neither path installs the App into any Workspace.

The App owns the repository-local official avatar
`web/public/app-icons/arxiv.png`.

## 2. Commands

The Builtin exposes exactly two read-only, idempotent Actions:

```text
agentteam exec arxiv search '{"query":"au:del_maestro AND ti:checkerboard","limit":10,"sortBy":"submittedDate"}'
agentteam exec arxiv get '{"id":"1605.08386v1"}'
```

There is no server-side PDF or source download Action. Every Paper result
includes `pdfUrl` and `sourceUrl` when available. The Agent may download those
URLs from its own runtime and network identity.

### 2.1 `search`

Input:

- `query`: optional unencoded arXiv query, at most 2048 Unicode characters;
- `idList`: optional non-empty list of at most 100 modern or legacy arXiv IDs,
  intersected with `query` according to the arXiv API;
- `offset`: optional non-negative result offset, default `0`;
- `limit`: optional integer from `1` through `20`, default `10`;
- `sortBy`: `relevance`, `lastUpdatedDate`, or `submittedDate`, default
  `relevance`;
- `sortOrder`: `ascending` or `descending`, default `descending`.

At least one of non-empty `query` or non-empty `idList` is required. The result
window must satisfy `offset + limit <= 30,000`, following the
current official API limit rather than the stale 300,000 value in the
`arxiv.py` 4.0.1 docstring.

Output is
`papers,totalResults,offset,returned,hasMore,nextOffset?`. Provider order is
preserved. `nextOffset` counts consumed Atom entries, including a malformed
individual entry that was skipped.

### 2.2 `get`

Input is one `id`. Modern IDs and legacy IDs are accepted, with or without a
`vN` suffix. An unversioned ID resolves the latest version; a versioned ID
requires that exact version. A valid missing paper returns `not_found`.

## 3. Paper result

Both Actions return the same Paper shape:

```text
id, entryUrl, publishedAt, updatedAt, title,
authors[{name,affiliations}], abstract,
comment?, journalRef?, doi?, primaryCategory, categories,
links[{href,title?,rel?,contentType?}], pdfUrl?, sourceUrl?
```

Parsing follows `arxiv==4.0.1`:

- Atom, OpenSearch, and arXiv namespaces are recognized;
- timestamps are timezone-aware UTC values;
- title whitespace is folded;
- all author affiliations and categories are preserved;
- `pdfUrl` is the first link with `title="pdf"`;
- `sourceUrl` is derived from the PDF URL by replacing `/pdf/` with `/src/`;
- an entry missing `id`, `updated`, or `published`, or carrying an invalid ID or
  timestamp, is skipped without discarding valid sibling entries;
- a malformed feed envelope fails the whole call.

## 4. Egress and errors

The server issues one GET to the fixed
`https://export.arxiv.org/api/query` endpoint with standard URL encoding.
Agent input cannot choose an endpoint, URL, method, header, or proxy. The HTTP
client ignores ambient proxy configuration, rejects redirects, applies bounded
timeouts, and reads at most 2 MiB of Atom.

The HTTP client, Atom parser, normalized types, and error mapping are owned by
`internal/extension/providers/arxiv`; arXiv has no separate reusable domain service under
`internal/service`.

Stable errors are:

- invalid input or arXiv HTTP 400: `validation`;
- valid missing `get` target: `not_found`;
- arXiv HTTP 429: `rate_limited`;
- other HTTP, network, timeout, oversized body, or XML/envelope errors:
  `upstream_failure`;
- `context.Canceled` and `context.DeadlineExceeded` remain distinguishable.

Per the approved V1 scope, AgentTeam does not add a proactive rate limiter. It still
classifies an upstream 429 without exposing upstream response content.

## 5. Migration and compatibility

The first upgrade reserves the `arxiv` product slug without destructive
rewrites. A historical custom App using that slug keeps its immutable ID and
dependent rows while its App, version manifest, and Service Definition slug are
renamed deterministically to
`arxiv-legacy-<first-16-hex-of-SHA256(app-id)>`.

The migration is pure DML. It creates no table, temporary table, column, index,
or connection row. A canonical ID, slug, kind, version, Definition, or
capability collision fails closed through existing constraints. Down hides the
App and disables its Definition/capability without deleting Workspace state;
roll-forward restores the canonical metadata.

The connection policy is part of the Builtin runtime digest. Existing Builtins
retain the original connection-required digest projection; `arxiv` uses the
policy-bearing connection-none projection. Catalog projection and invocation
both require exactly zero active App connections for this Definition.
