Skill: aipoch-ai/patent-claim-mapper (version 1.0.0)

--- name: patent-claim-mapper description: Use when mapping patent claims to products, analyzing patent infringement, or preparing freedom-to-operate analyses. Compares patent claims against product features for biotech and pharmaceutical IP assessment. license: MIT skill-author: AIPOCH --- # Patent Claim Mapper Map patent claims to product features for infringement analysis, freedom-to-operate assessments, and competitive intelligence in biotech/pharma. ## When to Use - Use this skill when the task needs Use when mapping patent claims to products, analyzing patent infringement, or preparing freedom-to-operate analyses. Compares patent claims against product features for biotech and pharmaceutical IP assessment. - Use this skill for evidence insight tasks that require explicit assumptions, bounded scope, and a reproducible output format. - Use this skill when you need a documented fallback path for missing inputs, execution errors, or partial evidence. ## Key Features - Scope-focused workflow aligned to: Use when mapping patent claims to products, analyzing patent infringement, or preparing freedom-to-operate analyses. Compares patent claims against product features for biotech and pharmaceutical IP assessment. - Packaged executable path(s): `scripts/main.py`. - Reference material available in `references/` for task-specific guidance. - Structured execution path designed to keep outputs consistent and reviewable. ## Dependencies - `Python`: `3.10+`. Repository baseline for current packaged skills. - `dataclasses`: `unspecified`. Declared in `requirements.txt`. ## Example Usage ```bash cd "20260318/scientific-skills/Evidence Insight/patent-claim-mapper" python -m py_compile scripts/main.py python scripts/main.py --help ``` Example run plan: 1. Confirm the user input, output path, and any required config values. 2. Edit the in-file `CONFIG` block or documented parameters if the script uses fixed settings. 3. Run `python scripts/main.py` with the validated inputs. 4. Review the generated output and return the final artifact with any assumptions called out. ## Implementation Details See `## Workflow` above for related details. - Execution model: validate the request, choose the packaged workflow, and produce a bounded deliverable. - Input controls: confirm the source files, scope limits, output format, and acceptance criteria before running any script. - Primary implementation surface: `scripts/main.py`. - Reference guidance: `references/` contains supporting rules, prompts, or checklists. - Parameters to clarify first: input path, output path, scope filters, thresholds, and any domain-specific constraints. - Output discipline: keep results reproducible, identify assumptions explicitly, and avoid undocumented side effects. ## Quick Check Use this command to verify that the packaged script entry point can be parsed before deeper execution. ```bash python -m py_compile scripts/main.py ``` ## Audit-Ready Commands Use these concrete commands for validation. They are intentionally self-contained and avoid placeholder paths. ```bash python -m py_compile scripts/main.py python scripts/main.py --help ``` ## Workflow 1. Confirm the user objective, required inputs, and non-negotiable constraints before doing detailed work. 2. Validate that the request matches the documented scope and stop early if the task would require unsupported assumptions. 3. Use the packaged script path or the documented reasoning path with only the inputs that are actually available. 4. Return a structured result that separates assumptions, deliverables, risks, and unresolved items. 5. If execution fails or inputs are incomplete, switch to the fallback path and state exactly what blocked full completion. ## Quick Start ```python from scripts.claim_mapper import ClaimMapper mapper = ClaimMapper() # Map claims to product mapping = mapper.analyze( patent_claims="patent_claims.txt", product_description="product_spec.txt" ) ``` ## Core Capabilities ### 1. Claim Parsing ```python claims = mapper.parse_claims( patent_file="US10123456B2.pdf", independent_only=False ) ``` ### 2. Feature Mapping ```python mapping = mapper.map_to_product( claim="A monoclonal antibody that binds to PD-1...", product_features=product_specs ) ``` **Mapping Results:** - **Fully covered**: Product implements claim - **Partially covered**: Some elements present - **Not covered**: Claim element missing - **Questionable**: Requires legal review ### 3. Gap Analysis ```python gaps = mapper.identify_gaps( mapping_results, strategy="design_around" ) ``` ## CLI Usage ```text python scripts/claim_mapper.py \ --patent US10123456B2 \ --product product_spec.txt \ --output mapping_report.pdf ``` --- **Skill ID**: 213 | **Version**: 1.0 | **License**: MIT ## Output Requirements Every final response should make these items explicit when they are relevant: - Objective or requested deliverable - Inputs used and assumptions introduced - Workflow or decision path - Core result, recommendation, or artifact - Constraints, risks, caveats, or validation needs - Unresolved items and next-step checks ## Error Handling - If required inputs are missing, state exactly which fields are missing and request only the minimum additional information. - If the task goes outside the documented scope, stop instead of guessing or silently widening the assignment. - If `scripts/main.py` fails, report the failure point, summarize what still can be completed safely, and provide a manual fallback. - Do not fabricate files, citations, data, search results, or execution outcomes. ## Input Validation This skill accepts requests that match the documented purpose of `patent-claim-mapper` and include enough context to complete the workflow safely. Do not continue the workflow when the request is out of scope, missing a critical input, or would require unsupported assumptions. Instead respond: > `patent-claim-mapper` only handles its documented workflow. Please provide the missing required inputs or switch to a more suitable skill. ## References - [references/audit-reference.md](references/audit-reference.md) - Supported scope, audit commands, and fallback boundaries ## Response Template Use the following fixed structure for non-trivial requests: 1. Objective 2. Inputs Received 3. Assumptions 4. Workflow 5. Deliverable 6. Risks and Limits 7. Next Checks If the request is simple, you may compress the structure, but still keep assumptions and limits explicit when they affect correctness.

path: references/audit-reference.md
content: # Audit Reference

## Scope

- Skill: `patent-claim-mapper`
- Core purpose: Use when mapping patent claims to products, analyzing patent infringement, or preparing freedom-to-operate analyses. Compares patent claims against product features for biotech and pharmaceutical IP assessment.
- Use only within the documented workflow and category boundary defined in `SKILL.md`

## Supported Audit Paths

- `python -m py_compile scripts/main.py`
- `python scripts/main.py --help`

## Fallback Boundary

If required inputs are incomplete, the skill should still return:

- the missing required inputs
- the steps that can still be completed safely
- assumptions that need confirmation before execution
- the next checks before accepting the final deliverable


path: requirements.txt
content: dataclasses


path: scripts/main.py
content: #!/usr/bin/env python3
"""
Patent Claim Mapper - Patent Infringement Risk Analysis Tool

Features:
1. Parse patent claim text
2. Extract technical features
3. Compare with product features
4. Generate infringement risk assessment report

Author: OpenClaw Skills Team
Version: 1.0.0
"""

import re
import json
import argparse
from dataclasses import dataclass, field, asdict
from typing import List, Dict, Optional, Tuple
from pathlib import Path
from datetime import datetime


@dataclass
class ClaimElement:
    """Technical element in patent claim"""
    text: str
    element_type: str  # 'apparatus', 'method', 'feature', 'limitation'
    keywords: List[str] = field(default_factory=list)
    dependencies: List[str] = field(default_factory=list)


@dataclass
class ClaimMapping:
    """Mapping between patent claim and product"""
    claim_element: str
    product_feature: Optional[str]
    mapping_status: str  # 'mapped', 'not_mapped', 'partial'
    similarity_score: float
    analysis_notes: str


@dataclass
class InfringementReport:
    """Patent infringement analysis report"""
    patent_number: str
    product_name: str
    overall_risk: str  # 'high', 'medium', 'low', 'clear'
    risk_score: float
    claim_mappings: List[ClaimMapping] = field(default_factory=list)
    recommendations: List[str] = field(default_factory=list)


class PatentClaimMapper:
    """Main class for patent claim mapping and analysis"""
    
    def __init__(self):
        self.claim_parser = ClaimParser()
        self.feature_extractor = FeatureExtractor()
        self.comparison_engine = ComparisonEngine()
    
    def analyze_infringement(self, patent_claims: str, product_description: str,
                            patent_number: str = "", product_name: str = "") -> InfringementReport:
        """Analyze potential patent infringement"""
        # Parse patent claims
        parsed_claims = self.claim_parser.parse(patent_claims)
        
        # Extract product features
        product_features = self.feature_extractor.extract(product_description)
        
        # Compare claims to features
        mappings = self.comparison_engine.compare(parsed_claims, product_features)
        
        # Calculate overall risk
        risk_score = self._calculate_risk_score(mappings)
        overall_risk = self._risk_level(risk_score)
        
        # Generate recommendations
        recommendations = self._generate_recommendations(mappings, overall_risk)
        
        return InfringementReport(
            patent_number=patent_number,
            product_name=product_name,
            overall_risk=overall_risk,
            risk_score=risk_score,
            claim_mappings=mappings,
            recommendations=recommendations
        )
    
    def _calculate_risk_score(self, mappings: List[ClaimMapping]) -> float:
        """Calculate infringement risk score"""
        if not mappings:
            return 0.0
        
        mapped_count = sum(1 for m in mappings if m.mapping_status == 'mapped')
        partial_count = sum(1 for m in mappings if m.mapping_status == 'partial')
        
        score = (mapped_count + 0.5 * partial_count) / len(mappings)
        return min(score, 1.0)
    
    def _risk_level(self, score: float) -> str:
        """Convert score to risk level"""
        if score >= 0.8:
            return "high"
        elif score >= 0.5:
            return "medium"
        elif score >= 0.2:
            return "low"
        else:
            return "clear"
    
    def _generate_recommendations(self, mappings: List[ClaimMapping], 
                                  risk_level: str) -> List[str]:
        """Generate recommendations based on analysis"""
        recommendations = []
        
        if risk_level == "high":
            recommendations.append("Consider design-around options for mapped elements")
            recommendations.append("Consult patent attorney for invalidity analysis")
        elif risk_level == "medium":
            recommendations.append("Investigate alternative implementations")
            recommendations.append("Monitor patent family for related claims")
        
        unmapped = [m for m in mappings if m.mapping_status == 'not_mapped']
        if unmapped:
            recommendations.append(f"Review {len(unmapped)} unmapped claim elements")
        
        return recommendations


class ClaimParser:
    """Parse patent claim text into structured elements"""
    
    def parse(self, claims_text: str) -> List[ClaimElement]:
        """Parse claim text into elements"""
        elements = []
        
        # Split into individual claims
        claims = re.split(r'\n\s*\d+\.', claims_text)
        
        for claim_text in claims:
            if not claim_text.strip():
                continue
            
            # Extract claim elements
            element = ClaimElement(
                text=claim_text.strip(),
                element_type=self._classify_type(claim_text),
                keywords=self._extract_keywords(claim_text)
            )
            elements.append(element)
        
        return elements
    
    def _classify_type(self, text: str) -> str:
        """Classify claim type"""
        text_lower = text.lower()
        if 'method' in text_lower or 'process' in text_lower:
            return 'method'
        elif 'apparatus' in text_lower or 'system' in text_lower:
            return 'apparatus'
        elif 'composition' in text_lower:
            return 'composition'
        return 'feature'
    
    def _extract_keywords(self, text: str) -> List[str]:
        """Extract key technical terms"""
        # Simple keyword extraction
        words = re.findall(r'\b[A-Z][a-z]+\b', text)
        return list(set(words))[:10]  # Return unique keywords


class FeatureExtractor:
    """Extract features from product description"""
    
    def extract(self, description: str) -> List[Dict]:
        """Extract product features"""
        features = []
        
        # Split into sentences
        sentences = re.split(r'[.!?]+', description)
        
        for sent in sentences:
            if not sent.strip():
                continue
            
            feature = {
                'text': sent.strip(),
                'keywords': self._extract_keywords(sent),
                'technical_terms': self._extract_technical_terms(sent)
            }
            features.append(feature)
        
        return features
    
    def _extract_keywords(self, text: str) -> List[str]:
        """Extract keywords from text"""
        words = re.findall(r'\b[A-Z][a-z]+\b', text)
        return list(set(words))[:10]
    
    def _extract_technical_terms(self, text: str) -> List[str]:
        """Extract technical terminology"""
        # Look for compound technical terms
        terms = re.findall(r'\b[A-Z][a-z]+(?:\s+[a-z]+){1,3}\b', text)
        return list(set(terms))[:5]


class ComparisonEngine:
    """Compare patent claims to product features"""
    
    def compare(self, claims: List[ClaimElement], 
                features: List[Dict]) -> List[ClaimMapping]:
        """Compare claims to features"""
        mappings = []
        
        for claim in claims:
            best_match = self._find_best_match(claim, features)
            
            mapping = ClaimMapping(
                claim_element=claim.text[:100],  # Truncate for readability
                product_feature=best_match['text'] if best_match else None,
                mapping_status=best_match['status'] if best_match else 'not_mapped',
                similarity_score=best_match['score'] if best_match else 0.0,
                analysis_notes=best_match.get('notes', '')
            )
            mappings.append(mapping)
        
        return mappings
    
    def _find_best_match(self, claim: ClaimElement, 
                         features: List[Dict]) -> Optional[Dict]:
        """Find best matching feature for claim element"""
        best_match = None
        best_score = 0.0
        
        for feature in features:
            score = self._calculate_similarity(claim, feature)
            
            if score > best_score:
                best_score = score
                best_match = feature
                best_match['score'] = score
                
                # Determine mapping status
                if score >= 0.7:
                    best_match['status'] = 'mapped'
                elif score >= 0.4:
                    best_match['status'] = 'partial'
                else:
                    best_match['status'] = 'not_mapped'
        
        return best_match
    
    def _calculate_similarity(self, claim: ClaimElement, 
                             feature: Dict) -> float:
        """Calculate similarity between claim and feature"""
        claim_keywords = set(claim.keywords)
        feature_keywords = set(feature.get('keywords', []))
        
        if not claim_keywords or not feature_keywords:
            return 0.0
        
        # Jaccard similarity
        intersection = claim_keywords & feature_keywords
        union = claim_keywords | feature_keywords
        
        return len(intersection) / len(union) if union else 0.0


def main():
    parser = argparse.ArgumentParser(description="Patent Claim Mapper")
    parser.add_argument("--patent-claims", required=True, help="Patent claims text file")
    parser.add_argument("--product-description", required=True, help="Product description file")
    parser.add_argument("--patent-number", help="Patent number for reference")
    parser.add_argument("--product-name", help="Product name")
    parser.add_argument("--output", default="infringement_report.json", help="Output file")
    
    args = parser.parse_args()
    
    # Load files
    with open(args.patent_claims, 'r') as f:
        claims_text = f.read()
    
    with open(args.product_description, 'r') as f:
        product_text = f.read()
    
    # Analyze
    mapper = PatentClaimMapper()
    report = mapper.analyze_infringement(
        claims_text, 
        product_text,
        patent_number=args.patent_number or "",
        product_name=args.product_name or ""
    )
    
    # Save report
    with open(args.output, 'w') as f:
        json.dump(asdict(report), f, indent=2)
    
    print(f"Infringement Analysis Complete")
    print(f"Overall Risk: {report.overall_risk.upper()}")
    print(f"Risk Score: {report.risk_score:.1%}")
    print(f"Report saved: {args.output}")


if __name__ == "__main__":
    main()


Security scan verdicts — ClawScan: clean | static analysis: clean | VirusTotal: clean | SkillSpector: suspicious (MEDIUM)