Skip to content

feat(fto): claim tree, features and all-elements claim chart model (ADR 0008) - #19

Merged
corlin merged 1 commit into
mainfrom
feat/fto-claim-chart
Oct 4, 2026
Merged

corlin merged 1 commit into
mainfrom
feat/fto-claim-chart

Conversation

@corlin

@corlin corlin commented Oct 4, 2026

Copy link
Copy Markdown
Owner

What

The core FTO domain model, as pure functions tested on real US/EP claims. No API, DB or UI yet (ADR 0008).

Module Purpose
modules/fto/us_claims.py US claims from the USPTO grant full-text XML
modules/fto/ep_claims.py EP claims from EPO OPS
modules/fto/model.py Claim tree, features with exact source spans, dependency paths, chart
modules/fto/chart.py Per-feature findings → claim-level conclusions (all-elements rule)

Claim sources (verified)

  • US: each ODP record links a per-patent grant XML (PTGRXML, same API key). It was present for 60/60 sampled patents (2007–2026). Numbering and dependencies come from the office's markup. The initial features follow the nested <claim-text> clauses, with exact character spans, including <b>1</b>. preambles.
  • EP: OPS returns flat paragraphs per language. Numbering and dependencies are parsed from wording (ranges, "or", "any preceding claim") and marked ops_text_parsed.
    • OPS does not state the language of the proceedings, so EPC Art. 70(1) authenticity is left unknown. Every EP set warns that it may be a translation; nothing is inferred.

Conclusions

  • Features start as proposed. Drafters' clauses aren't the same as legal claim elements. A path with unconfirmed features never concludes "reads", and features over 400 characters are flagged for splitting.
  • Findings: literally_present / present_by_equivalent / absent / undetermined. Equivalence is a supplied professional judgment, never computed.
  • All-elements rule, with multiple-dependent claims as alternatives (37 CFR 1.75(c); EPC Rule 43(4)): each alternative is its own path, and the claim reads if any path reads.
    • ⚠️ Self-correction: my first version merged parent features cumulatively. That over-requires features and could wrongly report "does not read", the dangerous direction. Fixed and tested.
  • Exact computation without enumerating paths: EP-1819002-B1 claim 15 has 2,304 paths. A randomized test (150 scenarios × 15 claims) matches full enumeration exactly.
  • Results are risk indications, not legal opinions. Jurisdiction rule packs (scope of equivalents, prosecution history estoppel) are still to come and need professional sign-off.

Verification

  • Checked by hand against the text: claim counts (EP 15/8/6, US 14/16/11/7) and every EP dependency.
  • Mutation check: changing "doesn't read only if all alternatives fail" to "any fails" breaks the targeted test and the property test.
  • Fixtures are public claims of 7 coverage-set patents (provenance in fixtures/fto/README.md).

Validation

  • .venv/bin/pytest apps/api/tests/unit/: 413 passed (14 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

Next

Claim-text acquisition adapters (US XML, EP OPS) with storage; product-feature input; feature confirmation workflow; then API/UI and jurisdiction rule packs.

🤖 Generated with Claude Code

…DR 0008)

Pure domain model for FTO comparison, tested on real US/EP claims (no
API, DB or UI yet).

Claim sources (verified 2026-10-04):
- US: per-patent grant full-text XML (PTGRXML) via ODP
  grantDocumentMetaData.fileLocationURI, present for 60/60 sampled
  patents (2007-2026). Numbering and dependencies come from office markup
  (<claim num>, <claim-ref>); initial features follow nested <claim-text>
  clauses, with exact character spans (handles <b>1</b>. preambles).
- EP: EPO OPS claims, flat paragraphs per language. Numbering and
  dependencies are parsed from wording (ranges, "or", "any preceding
  claim") and marked ops_text_parsed. OPS states no language of the
  proceedings, so EPC Art. 70(1) authenticity is left unknown and every EP
  set warns that it may be a translation.

Model and conclusions:
- Features start as "proposed" (clause structure is not legal claim
  elements); a path with unconfirmed features never concludes "reads".
- Findings: literally_present / present_by_equivalent / absent /
  undetermined; equivalence is a supplied professional judgment, never
  computed.
- All-elements rule. Multiple-dependent references are alternatives
  (37 CFR 1.75(c); EPC Rule 43(4)): each is its own path, and the claim
  reads if any path reads. My first version merged parent features
  cumulatively, which over-requires features and could wrongly say "does
  not read" (the dangerous direction). Fixed.
- Conclusions are computed exactly by recursion over claims instead of
  path enumeration (EP-1819002-B1 claim 15 has 2,304 paths). A randomized
  test (150 scenarios x 15 claims) matches full enumeration exactly.

Hand-checked against the text: claim counts (15/8/6 EP, 14/16/11/7 US)
and every EP dependency. Mutation check: switching "all alternatives fail"
to "any fails" breaks the targeted test and the property test.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@corlin
corlin merged commit d1f5990 into main Oct 4, 2026
1 check passed
corlin added a commit that referenced this pull request Oct 4, 2026
## What

Claim text for the comparison model (#19), stored with the ADR 0006
pattern. The design is in **ADR 0009**.

- Global, append-only table `patent_claim_documents` (worker
SELECT+INSERT, app SELECT, no tenant columns).
- `ClaimDocumentFetcher` (worker): fetch → archive the raw bytes →
record. A source failure writes nothing; "not found" is recorded.

## US: two-step fetch with safety checks (all verified live)

1. ODP lookup by patent number →
`grantDocumentMetaData.fileLocationURI`.
2. That URL answers with **one 302 to a signed, time-limited
`data.uspto.gov` URL**.

The client follows only USPTO dataset URLs, and only a redirect to
`data.uspto.gov/files/` (SSRF guard). It **sends the API key only to
`api.uspto.gov`**, and records the redirect **without its signature**,
which is a temporary credential.

Only the `<claims>` element goes into the DB. The **full XML names
inventors**, so it's archived in object storage under the open ADR 0006
retention question.

## EP

OPS claims for the exact publication. A 404 (an A1 publication, or an
unknown number) is recorded as `not_found`.

## What the stored text represents (important for FTO)

- **US = claims as granted.** Reexamination, IPR and correction
certificates can later amend or cancel claims, and they're **not** in
the grant XML. Stored as `text_represents = "as_granted"`.
- **EP = that specific publication.** A `B2`/`B3` replaces the `B1`
text. Stored as `"as_published"`.
- The US `fileCreateDateTime` is a **file** time, not a data date (a
2007 grant shows 2024-09-29).
- Fetching post-grant changes (PTAB/reexamination; automatic selection
of B2/B3) is follow-up work.

## Verification
- **Live:** all **16** coverage-set patents fetched and parsed, with
**no parse failures**. The only warnings are informational: "preceding
claims" ×31 (expanded into alternatives) and long features ×9.
- **Unit tests:** allowlist, redirect host, no leakage of the key or
signature, EP 404.
- **Postgres tests:** grants; US and EP end to end with the stored text
**parsed back by the ADR 0008 parsers**; three not-found shapes;
failures write nothing.

## Validation
- `./scripts/test-postgres.sh`: **111 passed** (+8)
- `.venv/bin/pytest apps/api/tests/unit/`: 417 passed (+4)
- `pnpm test:web`: 62 passed; `pnpm build:web`: ok
- `verify-source-lock`, `verify-scaffold`, `verify-p0-02`,
`verify-p0-e2e`, `docker compose config`: ok

🤖 Generated with [Claude Code](https://claude.com/claude-code)
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