Skip to content

test(legal-status): 17-case rule-coverage scenario (live + offline); handle US reissues - #18

Merged
corlin merged 1 commit into
mainfrom
test/legal-status-coverage-set
Oct 4, 2026
Merged

corlin merged 1 commit into
mainfrom
test/legal-status-coverage-set

Conversation

@corlin

@corlin corlin commented Oct 4, 2026

Copy link
Copy Markdown
Owner

Why

Per your direction: small, scenario-based test data instead of a full corpus load. No BigQuery: BigQuery bills by columns scanned, so even a 20-patent query would scan about 59 GB. The data comes from the USPTO/EPO APIs instead.

The coverage set

fixtures/scenarios/legal-status-coverage.json: 17 real public patents, one per rule. Expected values were derived by hand from the raw source records, not by running the code under test, and each case records its basis.

US EP
lapsed for non-payment lapsed in all designated states (DE/FR/GB)
term start from a DIV parent (ODP still says "Patented Case"; expired 2023) reinstated (IT, 2011), then lapsed again (2015)
PCT national stage: expired / in force a state with no reports (LI) is flagged, not assumed
provisional parent doesn't start the term fees paid near the end of the term (expires 2027-01-31)
terminal disclaimer flagged unitary effect flagged
PTA unknown flagged B2 after opposition, expired (B1/B2 share one INPADOC record)
largest PTA (1,220 days) → 2036-06-19
continuation + 1,171 days PTA → 2038-09-06
reissue → undetermined
not found → lookup recorded, no status
  • Offline: test_legal_status_coverage.py checks the 16 fixture cases plus the manifest itself (it requires a basis for every case).
  • Live: scripts/run-legal-status-scenario.py runs the manifest against the real APIs (17 calls) and reports drift instead of hiding it. Run on 2026-10-04: 17/17 match. --record also stores the results through LegalStatusRefresher.

Gap found and fixed: US reissues

A reissued patent runs for the remaining term of the original patent (35 U.S.C. §251). The code computed the term from the reissue's own later filing date, which overstated it. Now:

  • Reissues (applicationTypeCategory: REISSUE, confirmed in ODP) are undetermined with that reason until the original patent is looked up. ODP gives it as parent code REI.
  • The refresher accepts US-RE… numbers (ODP format RE49000 confirmed).
  • RULES_VERSION → legal-status-rules/2. The extract keeps applicationTypeCategory, and the existing US fixtures were regenerated (diff: the new field only).

A mutation check confirmed the reissue case fails without the fix.

Notes

  • The reissue example (RE49000) isn't in H01M/H02J; it was chosen to test the rule, not the field. ODP stops paging after 1,100 results, and none of those reissues were in scope.
  • --record wasn't run live: no PatentEvidence database is running on this machine. Its write path is covered by the Postgres integration tests.
  • ODP lists only a patent's immediate parent. An earlier grandparent would move expiry earlier, which is the safe direction; this is noted in the manifest.

Validation

  • .venv/bin/pytest apps/api/tests/unit/: 399 passed (17 new)
  • ./scripts/test-postgres.sh: 103 passed
  • pnpm test:web: 62 passed; pnpm build:web: ok
  • verify-source-lock, verify-scaffold, verify-p0-02, verify-p0-e2e, docker compose config: ok
  • Live scenario: 17/17

🤖 Generated with Claude Code

… handle reissues

Small, scenario-based test data instead of a full corpus load: 17 real
public patents, each chosen to exercise one rule. Expected values were
derived by hand from the raw ODP / EPO OPS records, not by running the
code under test, and each case records its basis in
fixtures/scenarios/legal-status-coverage.json.

Cases: US lapsed; term start from a DIV parent and from PCT national
stage (expired and in force); provisional parent excluded; terminal
disclaimer flagged; PTA unknown; largest PTA (1,220 days); continuation
with a large PTA; reissue; not found. EP lapsed in all designated states;
reinstated then lapsed; a state with no reports flagged; fees paid near
term end; unitary effect; B2 after opposition, expired (B1 and B2 share
one INPADOC record).

- Offline: test_legal_status_coverage.py checks all 16 fixture cases plus
  the manifest itself.
- Live: scripts/run-legal-status-scenario.py runs the manifest against the
  sources (17 API calls, no BigQuery) and reports any drift; --record
  stores the results through LegalStatusRefresher. Live run on
  2026-10-04: 17/17 match.

Gap found and fixed: a US reissue runs for the remaining term of the
original patent (35 U.S.C. 251), but its term was computed from the
reissue's own later filing date, overstating it. Reissues
(applicationTypeCategory REISSUE) are now undetermined with that reason
until the original is looked up, and the refresher accepts US-RE... numbers
(ODP format RE49000 confirmed). The extract keeps applicationTypeCategory;
RULES_VERSION becomes legal-status-rules/2. Existing US fixtures were
regenerated with the current extract (diff: the new field only).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@corlin
corlin merged commit 0a42ed9 into main Oct 4, 2026
1 check passed
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