# patent-client-agents TODO

Things flagged but not yet done. Sorted by how much they'd hurt if we
left them unchecked. Add items as you find them; check them off in PRs
that fix them.

## External blockers (waiting on someone)

Connector work blocked on a response from an outside party (API key
issuance, support ticket, ToS clarification). Check periodically —
when these clear, the downstream task usually unblocks in days, not
weeks.

- [ ] **UPC CMS Public Read API enrollment.** Awaiting a response from
      UPC IT support on how external developers obtain OAuth2 client
      credentials for the read-only Public API at
      `api-prod.unified-patent-court.org`. Auth scheme is documented
      (OAuth2 + Bearer JWT, token endpoint at 1800s lifetime) but the
      enrollment path is documented nowhere on UPC public pages —
      probes show the host rejects TLS handshakes from arbitrary
      clients before any HTTP layer, suggesting an IP allowlist
      and/or mTLS in front of OAuth. Investigation summary in
      `research/connectors/upc.md` §14; verbatim Athena-form ticket
      body in §14.6 of that file. **Ticket submitted 2026-05-14**
      via Safari (Chrome had been triggering the formcreator
      plugin's conditional-reveal breakage). Blocks task: `upc_cms`
      connector build. Watch for response — turnaround on UPC IT
      tickets is unspecified, expect days to weeks.

- [ ] **IP Australia developer API key.** Awaiting issuance of a
      client_id / client_secret pair from IP Australia's developer
      portal (`portal.api.ipaustralia.gov.au`) to access the
      OAuth2-secured Patents / Trade Marks / Designs Search APIs.
      Blocks task: `ip_australia` connector build, which is otherwise
      the highest-leverage Tier-1+2 connector on the roadmap
      (four IP rights from one OAuth stack + weekly CC-BY bulk dumps
      via IP RAPID — see `research/sprint_1.md` §"Connector 1:
      `ip_australia`"). Last checked: 2026-05-13.

## User-decision / enablement

- [x] **Publish `patent-client-agents` to PyPI.** Completed before
      v0.21.0. Docs and plugin install paths now use the PyPI package
      form (`pip install patent-client-agents` /
      `uvx --from patent-client-agents[mcp]==<version>`).

- [x] **Stand up the public remote MCP demo.** Hosted endpoint is live
      at `https://mcp.patentclient.com/mcp` with Google OAuth and
      public-demo rate limits. Keep credential-gated BYOK families off
      the hosted demo unless the upstream terms permit shared-key proxy
      operation.

- [ ] **CI auto-deploy on merge to `main`.** Cloud Build trigger on the
      deploy repo pointing at `cloudbuild.yaml`. Needs the GCP project to
      exist and `roles/run.admin` + `roles/artifactregistry.writer` granted
      to the Cloud Build SA.

## Known issues (deferred)

- [ ] **Cold-start latency on first plugin use.** First `uvx` invocation
      after `/plugin install patent-client-agents@patent-client-agents` downloads ~100 packages and takes ~30
      seconds. Subsequent invocations hit uv's cache and are ~1s. Consider
      pre-warming the cache at plugin-add time (via a Claude Code install
      hook, if one ever gets blessed) or leaning on the skill-first UX
      where the agent can answer before the MCP is cold-started.

## Near-term roadmap discipline

- [ ] **Promote provenance compliance by category.** Current coverage:
      representative tests for `registered_ip`, `adjudicative_records`,
      and `substantive_law` in `tests/test_provenance_contracts.py`,
      plus fees-specific effective-date enforcement in
      `tests/fees/test_envelope_compliance.py`. Next step: connect
      `coverage/sources.yaml` source rows to MCP tool modules so CI can
      require category-specific provenance on every shipped tool, not
      just representative helpers.

- [ ] **Treat substitution rules as roadmap gates.** Before building any
      new national connector, record why higher-layer coverage (EPO
      OPS/INPADOC, WIPO Lex, EUIPO, Google Patents, existing static-law
      corpora) is insufficient. The default answer should be "do not
      build" unless the national source uniquely adds prosecution,
      office actions, assignments, national TM/design register data, or
      fresher authoritative status.

- [ ] **Separate BYOK work from hosted-demo work.** Credential-gated
      connectors should stay env-gated and absent from the public demo
      unless the upstream terms permit shared-key proxy operation.
      Hosted work should prioritize always-on/public sources, corpora,
      fees, and smoke-test coverage.

## Hosted MCP reliability

- [ ] **Add a hosted smoke-check workflow.** Minimum checks: remote MCP
      handshake, tool-count floor, one no-auth registered-IP query, one
      corpus-backed substantive-law query, one fee lookup, and one
      download/resource template check. Run on a schedule and after
      deploys against `https://mcp.patentclient.com/mcp`.

- [ ] **Add corpus-bootstrap health reporting.** Remote deploys should
      expose whether each required corpus materialized, its SHA/version,
      and its `corpus_synced_at` value. A missing corpus should fail
      deploy health checks before users hit the tool.

- [ ] **Publish operational limits in one place.** Keep public-demo rate
      limits, credential-gated omissions, WAF/token refresh behavior,
      and confidentiality warnings in a single hosted-demo section, then
      link README and installation docs to it.

## Nice to have

- [ ] **JPO module test coverage.** Credentials are restricted, so the
      pragmatic approach is recording VCR cassettes once against a known
      good fixture set and replaying thereafter.

## Done this session (2026-05-13)

- ✓ **User-facing docs updated for the MPEP/TMEP corpus surface.**
  Eight files touched: `mpep/docs/usage.md` (served as
  `resource://mpep/usage`) rewritten end-to-end; `catalog/sources/{mpep,tmep}.md`
  swapped the obsolete eMPEP/eTMEP endpoint tables for a
  SQLite/FTS5 backend description; `docs/api/{mpep,tmep}.md` gained a
  "First-time setup" + "Cloud deploys" section; the
  `skills/ip_research/references/{mpep,tmep}.md` references picked up a
  "Backend" section so agents know the corpus must exist; and
  `docs/installation.md` got a "MPEP / TMEP corpus setup" subsection
  replacing the misleading "MPEP/TMEP still work without keys" line.

- ✓ **FedReserve VCR cassette refreshed (law-tools repo).** The
  cassette had a single recorded interaction but the test exercises
  three GETs (testing that "SR1106" / "11-6" / "SR 11-6" all normalize
  to the same URL) — replay was failing with "cassette exhausted." Re-
  recorded against live www.federalreserve.gov for SR 11-6, gitleaks
  scan clean. Same commit repaired a cross-repo break in
  law-tools/tests/test_section_lookup.py that was importing the deleted
  `patent_client_agents.mpep.transformers` — removed the obsolete
  TestMpepTocParsing class and redirected TestTmepLiveSectionLookup to
  the corpus-backed `patent_client_agents.tmep`.

- ✓ **JPO retry consolidated onto `default_retryer`.** The bespoke
  `AsyncRetrying(...)` block in `jpo/client.py:_raw_request` now
  delegates to `mcp_data_core.resilience.default_retryer` for backoff
  and retry-filter behavior; rate-limit acquire, token refresh on
  401/403, and 429→`RateLimitError` mapping stay inline because they
  encode JPO-specific protocol details (not generic resilience). Side
  benefit: `default_retryer`'s filter is more selective than the
  previous "retry on any exception", so plain `ApiError` (4xx and 5xx
  responses with non-retryable codes) no longer burns three attempts.
  Test suite drops from 9.01s → 3.93s for `tests/jpo/`.

- ✓ **TMEP corpus replaces all eTMEP HTTP calls.** Same pattern as the
  MPEP work earlier in the session: `tmep/corpus/{schema,db,build}.py`
  + new console script `patent-client-agents-build-tmep-corpus`,
  TmepClient rewritten as a corpus reader, conftest.py + test_client.py
  with a 5-section fixture corpus. Live scrape produces 1,751 sections
  across all 19 TMEP chapters (100–1900) in ~2 minutes, 16MB SQLite.
  Runtime resolves the corpus via `TMEP_CORPUS_PATH` env var →
  `~/.cache/patent_client_agents/tmep.db` → `CorpusUnavailable`.
  Deleted dead `tmep/{transformers,utils}.py` + `test_transformers.py`.

- ✓ **MPEP corpus replaces all eMPEP HTTP calls.** USPTO's
  `/RDMS/MPEP/search` is currently broken externally regardless of UA
  (verified by probing with library UA, full Chrome UA + Referer,
  cookies, HTTP/2). Built a SQLite/FTS5 corpus pipeline that ships
  with the wheel as a buildable artifact, not the data itself:
    - `src/patent_client_agents/mpep/corpus/{__init__,schema,db,build}.py`
      — schema (sections + FTS5 external-content index), read-side
      `CorpusDB.open()` with env-var/cache-path/error resolution, and
      a single-file scraper+parser+writer (BFS-crawls eMPEP's
      `/content` endpoint from one seed, dedupes by section href).
    - Console script `patent-client-agents-build-mpep-corpus` wired
      into `pyproject.toml`; produces `mpep.db` in ~4 minutes against
      live USPTO. Initial snapshot: 3,013 sections, 51MB, all 29 MPEP
      chapters (100 through 2900) plus 832 appendix entries.
    - `MpepClient` in `src/patent_client_agents/mpep/client.py`
      rewritten: same public surface (`search`, `get_section`,
      `resolve_section_href`, `list_versions`) now reads from
      SQLite/FTS5. Lazy-opens the corpus; raises `CorpusUnavailable`
      with build instructions if the DB is missing.
    - Runtime corpus resolution priority:
      1. `MPEP_CORPUS_PATH` env var (cloud deploys).
      2. `~/.cache/patent_client_agents/mpep.db` (local dev).
      3. `CorpusUnavailable` with a helpful build hint.
    - Tests rewritten end-to-end: `tests/mpep/conftest.py` builds a
      tiny 5-section fixture corpus once per session; `test_client.py`
      exercises 14 cases (lookups, search syntax variants, pagination,
      version, missing-corpus error). Removed dead
      `tests/mpep/{test_transformers,test_mpep_transformers,test_utils}.py`
      that tested the old HTTP transformers.
  Cloud-deploy story: build CLI runs at image build time, outputs to a
  known path, runtime points `MPEP_CORPUS_PATH` at it. The wheel stays
  small (~500KB) regardless of corpus size. Refresh = rerun the
  builder + redeploy.

- ✓ **TSDR `get_status` switched from ST96 XML to JSON.** USPTO's
  `casestatus/sn{serial}/info` now returns JSON by default; the XML
  parser threw `ParseError`, a bare `except` swallowed it, and every
  caller got a `TrademarkStatus` with only `serialNumber` set. Replaced
  `_parse_status_xml` in `src/patent_client_agents/uspto_tsdr/client.py`
  with `_parse_status_json` mapping the new shape
  (`trademarks[0].{status,parties,gsList,prosecutionHistory}`) onto the
  existing `TrademarkStatus` model. Dropped the silent except — parse
  errors now propagate. The XML import stays because
  `_parse_documents_xml` still consumes XML (documents endpoint
  unchanged).
- ✓ **TSDR test_get_status hardened.** Was only asserting
  `serial_number == "78787878"` — exactly what passed when every other
  field was None. Now asserts on `mark_text`, `filing_date`,
  `status_date`, `abandonment_date`, `mark_type`, owners, prosecution
  events, and goods/services counts so the all-null regression can't
  recur.
- ✓ **Stale TSDR `test_get_last_update` cassette refreshed** by copying
  the up-to-date JSON body from the `test_get_status` cassette (same
  URL, same serial). No live USPTO call required.

## Done this session (2026-04-23)

- ✓ Skill references/*.md refreshed — EP Register section stripped from
  epo_ops.md (5 nonexistent methods); `resolve_document_by_publication_number`
  renamed to `resolve_publication` in uspto_publications.md; JPO method
  names corrected.
- ✓ `docs/api/` backfilled — added uspto-odp.md, uspto-assignments.md,
  uspto-office-actions.md.
- ✓ Monorepo CLAUDE.md lists patent-client-agents as a library-backed skill.
- ✓ `patent-client-agents-mcp --help` / `--version` works (was a bare `mcp.run()`).
- ✓ `USPTO Publications _poll_print_job()` has a 5-minute bounded timeout.
- ✓ `Google Patents` retry loop uses `mcp_data_core.resilience.default_retryer`
  (consolidated from a bespoke `AsyncRetrying` instance).
- ✓ Module-level loggers + one meaningful `logger.warning()` at the XML-fallback
  path in USPTO applications / assignments / publications.
- ✓ Fixed latent bug: `from ...core.exceptions import NotFoundError` in
  `uspto_odp/clients/applications.py` (stale pre-extraction import that
  would have ImportError'd on first call).
- ✓ Skill `install.sh` prefers `uv` over `pip install --user`.
- ✓ Law-tools colline tests skip collection cleanly when `pdfplumber` is
  absent (pytest `collect_ignore_glob` gate, not a dep bump).
- ✓ Law-tools leaks scrubbed: LegiScan API key + 9 access_keys sanitized
  in the VCR cassette, VCR `filter_query_parameters` patched to catch
  `key=...` going forward, `.gitleaks.toml` allowlist for 3 known-safe
  findings (EDIS JWT example, Google Patents browser-inlined GCP key,
  placeholder `YOUR_*` strings). `gitleaks detect` → 0 leaks.
