AXLR / Catalog / law

clause_checklist

Clause checklist for a contract type (NDA, services, lease, employment, sale of goods, software licence, GDPR Article 28 data processing agreement): each clause's purpose and the questions to ask, with the legal reference for the DPA's mandatory contents. Given a contract text in English or Spanish, it reports each clause as found, with the matching snippet, or missing, by keyword and heading heuristics. Heuristic and not legal advice.

Install

/tools install clause_checklist
/tools install clause_checklist@1.0.0

In a console session, or let the model find it in normal mode: axlr_catalog describes it and axlr_install_tool asks for the same install card. The card reads catalog axlr · clause_checklist 1.0.0 · verified once the index signature, the manifest digest and every file digest check out. Then the model runs it with axlr_run_tool clause_checklist; each run is approved as a local_exec.

Arguments

One JSON object on stdin, validated against the schema before the program starts.

nametyperequireddefaultmeaning
contract_typestringnoone of "nda", "services", "lease", "employment", "sale_of_goods", "software_license", "dpa"
textstringnoThe contract text, inline
languagestringno"en"Clause titles and disclaimer. one of "en", "es"
list_typesbooleanno
Input schema (JSON)
{
  "type": "object",
  "additionalProperties": false,
  "properties": {
    "contract_type": {
      "type": "string",
      "enum": [
        "nda",
        "services",
        "lease",
        "employment",
        "sale_of_goods",
        "software_license",
        "dpa"
      ]
    },
    "text": {
      "type": "string",
      "description": "The contract text, inline"
    },
    "language": {
      "type": "string",
      "enum": [
        "en",
        "es"
      ],
      "default": "en",
      "description": "Clause titles and disclaimer"
    },
    "list_types": {
      "type": "boolean"
    }
  }
}

Example

Input

{
  "contract_type": "nda",
  "text": "This Agreement is made between Acme Ltd and Beta SL. Confidential Information means any information disclosed by one party. This Agreement is governed by the laws of England and Wales."
}

Output (stdout)

{
  "summary": {
    "found": 4,
    "missing": 11,
    "total": 15
  }
}

Files

The command the install card registers is python3 clause_checklist.py, run in the workspace with PATH and HOME as its only environment. Each file's SHA-256 is in the signed index; the console checks them before showing the card and again before writing.

clause_checklist.py · sha256 a1b4fc040eb619d314b3b25a305adddc5c6d653ca418a7d799f11d681b3bad2d
#!/usr/bin/env python3
"""Contract clause checklist per contract type (nda, services, lease, employment, sale_of_goods,
software_license, dpa): each clause's purpose and the questions to ask. With a contract text,
each clause is looked for by keyword and heading heuristics in English and Spanish and reported
found (with the matching snippet) or missing. The search is heuristic: a keyword found does not
mean the clause is adequate, and a clause worded differently can be missed. Not legal advice.

Input (JSON on stdin):
  contract_type  "nda" | "services" | "lease" | "employment" | "sale_of_goods" | "software_license" | "dpa"
  text           the contract text, inline (optional)
  language       "en" | "es"   clause titles (default en)
  list_types     true to list the contract types
"""
import json
import re
import sys
import unicodedata

DISCLAIMER = {"en": "Checklist for review, not legal advice: the clauses a contract needs depend on the law that governs it and on the deal.",
              "es": "Lista de comprobación orientativa, no es asesoramiento jurídico: las cláusulas necesarias dependen de la ley aplicable y del negocio."}
HEURISTIC = ("Heuristic keyword and heading search in English and Spanish: 'found' means a matching word was seen, not that the "
             "clause is complete or valid; 'missing' clauses may be worded differently. Read the contract.")

# id: (title en, title es, purpose, questions, keyword patterns on accent-free lower-case text)
C = {
    "parties": ("Parties and recitals", "Partes y exponen", "Identify who is bound, in what capacity, and the background of the deal.",
                ["Are the legal names, registration numbers and addresses correct?", "Who signs, and with what authority?"],
                [r"parties", r"between\b", r"recitals", r"whereas", r"\bpartes\b", r"reunidos", r"intervienen", r"exponen"]),
    "definitions": ("Definitions", "Definiciones", "Fix the meaning of terms used throughout.", ["Are key terms defined once and used consistently?"],
                    [r"definitions", r"defined terms", r"definiciones", r"se entendera por"]),
    "term_termination": ("Term and termination", "Duración y resolución", "Say when the contract starts and ends, how it renews and how either party can end it.",
                         ["Fixed term or indefinite? Automatic renewal?", "Termination for convenience, for breach, notice periods?", "What survives termination?"],
                         [r"\bterm\b", r"terminat", r"duracion", r"vigencia", r"resolucion", r"rescision", r"extincion", r"prorroga"]),
    "fees_payment": ("Price and payment", "Precio y forma de pago", "Set the price, invoicing, payment terms, taxes and late-payment consequences.",
                     ["Fixed price, rates or milestones?", "Payment period and late-payment interest?", "Are taxes (VAT) included or added?"],
                     [r"\bfees?\b", r"\bprice\b", r"payment", r"invoic", r"\bprecio\b", r"\bpago\b", r"factura", r"honorarios", r"retribucion"]),
    "confidentiality": ("Confidentiality", "Confidencialidad", "Protect non-public information exchanged under the contract.",
                        ["What is confidential and for how long?", "Who may receive it (employees, advisers)?"],
                        [r"confidential", r"confidencial", r"deber de secreto", r"\bsecreto\b"]),
    "personal_data": ("Personal data protection", "Protección de datos personales", "Allocate data protection roles and obligations (GDPR) where personal data is processed.",
                      ["Is either party a processor for the other (then a GDPR Art. 28 contract is needed)?", "Are international transfers involved?"],
                      [r"personal data", r"data protection", r"gdpr", r"datos personales", r"proteccion de datos", r"\brgpd\b", r"\blopdgdd\b"]),
    "ip": ("Intellectual property", "Propiedad intelectual e industrial", "Say who owns pre-existing and newly created IP and what licences are granted.",
           ["Who owns deliverables and work product?", "Is pre-existing IP licensed, and on what terms?"],
           [r"intellectual property", r"\bip\b rights", r"copyright", r"propiedad intelectual", r"propiedad industrial", r"derechos de explotacion"]),
    "warranties": ("Representations and warranties", "Manifestaciones y garantías", "Record what each party promises about facts, quality or authority.",
                   ["What is warranted and for how long?", "What is the remedy for breach of warranty?"],
                   [r"warrant", r"represent", r"garantia", r"manifestaciones", r"garantiza"]),
    "liability": ("Limitation of liability", "Limitación de responsabilidad", "Cap and exclude liability, and say what cannot be limited.",
                  ["Is there a cap, and how is it set?", "Are indirect or consequential losses excluded?", "Are fraud, wilful misconduct and death or injury carved out?"],
                  [r"limitation of liability", r"\bliabilit", r"responsabilidad", r"\bliable\b"]),
    "indemnity": ("Indemnities", "Indemnidades", "Allocate the cost of specific third-party claims or losses.",
                  ["Which claims are indemnified, and is there a cap?", "Procedure: notice, control of defence, cooperation?"],
                  [r"indemnif", r"indemn", r"hold harmless", r"mantener indemne"]),
    "force_majeure": ("Force majeure", "Fuerza mayor", "Excuse performance prevented by events beyond the parties' control.",
                      ["Which events count?", "Notice, mitigation and termination after a long event?"],
                      [r"force majeure", r"fuerza mayor", r"caso fortuito"]),
    "assignment": ("Assignment and subcontracting", "Cesión y subcontratación", "Control whether rights and obligations can be transferred or subcontracted.",
                   ["Is consent needed to assign? Change of control?", "May obligations be subcontracted, and who remains liable?"],
                   [r"assign", r"subcontract", r"\bcesion\b", r"ceder", r"subcontrat"]),
    "notices": ("Notices", "Notificaciones", "Say how formal communications must be given.", ["Addresses, e-mail accepted, when is notice received?"],
                [r"notices", r"notification", r"notificaciones", r"comunicaciones entre las partes"]),
    "entire_agreement": ("Entire agreement and amendments", "Acuerdo íntegro y modificaciones", "Exclude prior agreements and require amendments in writing.",
                         ["Do side letters or prior drafts survive?", "How can the contract be changed?"],
                         [r"entire agreement", r"amendment", r"acuerdo integro", r"totalidad del acuerdo", r"modificacion"]),
    "governing_law": ("Governing law and jurisdiction", "Ley aplicable y jurisdicción", "Choose the law that governs and the courts or arbitration that decide disputes.",
                      ["Which law and which courts or arbitral rules?", "Is the choice valid for consumers or employees, who may have mandatory protections?"],
                      [r"governing law", r"governed by", r"jurisdiction", r"arbitrat", r"ley aplicable", r"jurisdiccion", r"fuero", r"juzgados y tribunales", r"arbitraje", r"legislacion aplicable"]),
    "signatures": ("Signatures", "Firmas", "Execute the contract validly.", ["Are all parties signing, with dates and capacity?", "Electronic signature acceptable?"],
                   [r"signature", r"signed", r"in witness whereof", r"firma", r"firmado", r"en prueba de conformidad"]),
    # NDA
    "nda_definition": ("Definition of confidential information", "Definición de información confidencial", "Fix what information is protected, in any form.",
                       ["Is it mutual or one-way?", "Does it cover oral information and information marked later?"],
                       [r"confidential information", r"informacion confidencial"]),
    "nda_exclusions": ("Exclusions", "Exclusiones", "Exclude information that is public, already known, independently developed or lawfully received from third parties.",
                       ["Are the four standard exclusions there?", "Who bears the burden of proving an exclusion?"],
                       [r"exclusion", r"shall not include", r"does not include", r"publicly available", r"public domain", r"exclusiones", r"no se considerara", r"dominio publico"]),
    "nda_purpose_use": ("Permitted purpose and use", "Finalidad y uso permitido", "Limit use of the information to the stated purpose.",
                        ["Is the purpose specific enough?", "Who may receive it (need to know)?"],
                        [r"purpose", r"use solely", r"only for", r"finalidad", r"unicamente para", r"exclusivamente para"]),
    "nda_compelled": ("Compelled disclosure", "Revelación exigida por ley", "Allow disclosure required by law or court order, with notice to the discloser.",
                      ["Is prompt notice required so the discloser can object?"],
                      [r"required by law", r"court order", r"compelled", r"requerimiento", r"orden judicial", r"exigid[ao] por (la )?ley", r"autoridad competente"]),
    "nda_return": ("Return or destruction", "Devolución o destrucción", "Require return or destruction of information at the end or on request.",
                   ["Can archival back-ups be kept?", "Is a certificate of destruction required?"],
                   [r"return", r"destr", r"devolucion", r"devolver", r"destruccion", r"destruir"]),
    "nda_no_licence": ("No licence, no warranty", "Sin licencia ni garantía", "Make clear that disclosure grants no rights and no warranty of accuracy.",
                       ["Is the information provided 'as is'?"], [r"no licen[cs]e", r"as is", r"no rights", r"ninguna licencia", r"no otorga"]),
    "nda_remedies": ("Remedies", "Consecuencias del incumplimiento", "Provide injunctive relief and damages or a penalty for breach.",
                     ["Injunctive relief available? A penalty clause (cláusula penal) and is it enforceable?"],
                     [r"injunct", r"remed", r"damages", r"penalt", r"clausula penal", r"danos y perjuicios", r"incumplimiento"]),
    "nda_duration": ("Duration of the obligation", "Duración de la obligación de confidencialidad", "Say how long the obligation lasts after disclosure or termination.",
                     ["A number of years, or indefinite for trade secrets?"],
                     [r"\byears?\b", r"surviv", r"\banos\b", r"indefinid", r"duracion"]),
    # services
    "scope": ("Scope of services and deliverables", "Objeto y alcance de los servicios", "Describe what will be delivered, to what standard and by when.",
              ["Is there a statement of work?", "How are changes to scope agreed (change control)?"],
              [r"scope", r"services", r"statement of work", r"deliverable", r"\bobjeto\b", r"alcance", r"servicios", r"entregables"]),
    "service_levels": ("Service levels and acceptance", "Niveles de servicio y aceptación", "Set measurable standards, acceptance tests and credits for failures.",
                       ["Are SLAs measurable? Service credits?", "How and when are deliverables accepted?"],
                       [r"service level", r"\bsla\b", r"acceptance", r"niveles? de servicio", r"aceptacion"]),
    "insurance": ("Insurance", "Seguros", "Require cover for the risks the contract allocates.", ["Which policies, minimum amounts, evidence of cover?"],
                  [r"insurance", r"seguro", r"poliza"]),
    "non_solicitation": ("Non-solicitation", "No captación de personal", "Restrict hiring each other's staff during and after the contract.",
                         ["Is the restriction proportionate and lawful where applied?"], [r"solicit", r"no captacion", r"contratar al personal"]),
    # lease
    "premises": ("Description of the property", "Descripción del inmueble", "Identify the property, its annexes (parking, storage) and condition.",
                 ["Land registry and cadastral references?", "Inventory of contents and condition report?"],
                 [r"premises", r"property", r"\binmueble\b", r"vivienda", r"\blocal\b", r"finca", r"referencia catastral"]),
    "permitted_use": ("Permitted use", "Destino o uso", "Fix what the property may be used for.", ["Residential or business use? Is subletting or tourism use excluded?"],
                      [r"permitted use", r"\buse of the\b", r"\bdestino\b", r"\buso\b"]),
    "rent": ("Rent and updates", "Renta y actualización", "Set the rent, payment and any indexation.",
             ["Amount, payment date and method?", "How is rent updated (index, cap under the applicable law)?"],
             [r"\brent\b", r"\brenta\b", r"alquiler", r"actualizacion"]),
    "deposit": ("Deposit and guarantees", "Fianza y garantías", "Secure the tenant's obligations and say how the deposit is returned.",
                ["Legal deposit amount and where it is lodged?", "Additional guarantees (bank guarantee, guarantor)?"],
                [r"deposit", r"\bfianza\b", r"garantia adicional", r"aval"]),
    "expenses": ("Expenses, utilities and taxes", "Gastos, suministros y tributos", "Allocate utilities, community charges and property taxes.",
                 ["Who pays community fees and property tax?", "Are utilities in the tenant's name?"],
                 [r"utilities", r"service charge", r"expenses", r"gastos", r"suministros", r"\bibi\b", r"comunidad"]),
    "repairs": ("Maintenance and repairs", "Conservación y reparaciones", "Allocate repair obligations between landlord and tenant.",
                ["Which repairs are the landlord's and which the tenant's (minor wear and tear)?"],
                [r"repair", r"mainten", r"reparacion", r"conservacion", r"mantenimiento"]),
    "alterations": ("Alterations and works", "Obras", "Control works by the tenant and the landlord.", ["Is consent needed for works? Who keeps improvements?"],
                    [r"alteration", r"\bworks\b", r"\bobras\b", r"mejoras"]),
    "sublet": ("Assignment and subletting", "Cesión y subarriendo", "Control whether the tenant may assign or sublet.", ["Is written consent needed?"],
               [r"sublet", r"sub-?lease", r"assign", r"subarriendo", r"cesion del contrato"]),
    "handover": ("Return of the property", "Devolución del inmueble", "Set the condition on return, inspection and deposit settlement.",
                 ["Inspection on exit? Time to return the deposit?"], [r"surrender", r"vacat", r"\breturn\b", r"devolucion", r"entrega de llaves"]),
    # employment
    "position": ("Position, duties and place of work", "Puesto, funciones y lugar de trabajo", "Describe the job, professional group and where it is performed.",
                 ["Job title and professional classification?", "Remote work arrangements?"],
                 [r"position", r"duties", r"job title", r"place of work", r"puesto", r"funciones", r"grupo profesional", r"centro de trabajo", r"lugar de trabajo"]),
    "contract_duration": ("Start date and type of contract", "Fecha de inicio y modalidad", "State the start date and whether the contract is indefinite or temporary, with its legal cause.",
                          ["If temporary, is the legal cause stated and valid?"],
                          [r"commencement", r"start date", r"fixed[- ]term", r"indefinite", r"fecha de inicio", r"indefinid", r"temporal", r"duracion del contrato"]),
    "probation": ("Probation period", "Período de prueba", "Set a trial period within legal or collective-agreement limits.", ["Length allowed by law or the collective agreement?"],
                  [r"probation", r"trial period", r"periodo de prueba"]),
    "working_time": ("Working time", "Jornada y horario", "Set hours, schedule, overtime and time recording.", ["Full or part time? Distribution of hours? Overtime pay?"],
                     [r"working time", r"working hours", r"hours of work", r"overtime", r"jornada", r"horario", r"horas extra"]),
    "salary": ("Salary and benefits", "Retribución", "Set pay, its components, payment frequency and extra payments.",
               ["Gross annual salary and components?", "Does it meet the collective agreement and minimum wage?"],
               [r"salary", r"remuneration", r"\bwages?\b", r"compensation", r"salario", r"retribucion", r"pagas extra", r"nomina"]),
    "leave": ("Holidays and leave", "Vacaciones y permisos", "Set annual leave and other leave.", ["Days of annual leave? How are dates set?"],
              [r"holiday", r"annual leave", r"vacation", r"vacaciones", r"permisos"]),
    "collective_agreement": ("Applicable collective agreement", "Convenio colectivo aplicable", "Name the collective agreement that governs the relationship.",
                             ["Which agreement, and is the classification consistent with it?"], [r"collective agreement", r"convenio colectivo"]),
    "non_compete": ("Non-competition", "No competencia", "Restrict competition during or after employment, with compensation where required.",
                    ["Is a post-termination restriction limited in time and compensated as the law requires?"],
                    [r"non-?compet", r"no competencia", r"pacto de no concurrencia", r"concurrencia"]),
    "employee_privacy": ("Employee data protection information", "Información sobre protección de datos del trabajador", "Inform the employee about processing of their data (GDPR Art. 13).",
                         ["Is there an Art. 13 notice? Monitoring of devices or video surveillance disclosed?"],
                         [r"personal data", r"data protection", r"datos personales", r"proteccion de datos"]),
    "notice_period": ("Notice of termination", "Preaviso", "Set notice periods for resignation and dismissal.", ["Periods in law and in the collective agreement?"],
                      [r"notice period", r"\bnotice\b", r"preaviso"]),
    # sale of goods
    "goods": ("Goods, quantity and specifications", "Mercancías, cantidad y especificaciones", "Describe the goods precisely.", ["Specifications, quantities, tolerances, packaging?"],
              [r"\bgoods\b", r"products", r"specification", r"quantity", r"mercancia", r"productos", r"especificaciones", r"cantidad"]),
    "delivery": ("Delivery", "Entrega", "Set place, date and terms of delivery (e.g. an Incoterms rule).", ["Which Incoterms rule and version? Late-delivery consequences?"],
                 [r"deliver", r"incoterms", r"shipment", r"entrega", r"envio"]),
    "risk_title": ("Passing of risk and retention of title", "Transmisión del riesgo y reserva de dominio", "Say when risk and ownership pass.",
                   ["When does risk pass? Is title retained until payment?"],
                   [r"\brisk\b", r"\btitle\b", r"retention of title", r"riesgo", r"reserva de dominio", r"propiedad de la mercancia"]),
    "inspection": ("Inspection and notice of defects", "Inspección y denuncia de defectos", "Set the time and form to examine goods and give notice of non-conformity.",
                   ["Time limit to give notice of defects?"], [r"inspect", r"examin", r"non-?conform", r"inspeccion", r"defectos", r"vicios"]),
    "product_warranty": ("Warranty of conformity", "Garantía de conformidad", "Set the warranty period and remedies (repair, replacement, price reduction, termination).",
                         ["Warranty period and remedies? Exclusions?"], [r"warrant", r"conformity", r"garantia", r"conformidad"]),
    "cisg": ("International sale: CISG", "Compraventa internacional: CISG", "Decide whether the UN Convention on Contracts for the International Sale of Goods applies; it applies by default between parties in Contracting States unless excluded (Art. 6).",
             ["Is the CISG excluded or accepted expressly?"], [r"\bcisg\b", r"vienna convention", r"convencion de viena", r"united nations convention on contracts"]),
    # software licence
    "licence_grant": ("Licence grant and scope", "Concesión y alcance de la licencia", "Define what may be done with the software: users, devices, territory, term, exclusivity.",
                      ["Named users, concurrent or enterprise? Affiliates? Production and test use?"],
                      [r"grant", r"licen[cs]e", r"licencia", r"concede", r"otorga"]),
    "restrictions": ("Restrictions", "Restricciones de uso", "List prohibited acts (copying, reverse engineering, sublicensing) within what the law allows.",
                     ["Are restrictions consistent with mandatory exceptions (e.g. decompilation for interoperability)?"],
                     [r"restriction", r"shall not", r"reverse engineer", r"restricciones", r"no podra", r"ingenieria inversa"]),
    "support": ("Support and maintenance", "Soporte y mantenimiento", "Set updates, bug fixing, response times and support fees.", ["Which versions are supported, and for how long?"],
                [r"support", r"maintenance", r"updates", r"soporte", r"mantenimiento", r"actualizaciones"]),
    "ip_indemnity": ("IP infringement indemnity", "Indemnidad por infracción de derechos", "Protect the licensee against third-party IP claims about the software.",
                     ["Remedies: modify, replace, refund? Exclusions for combinations?"], [r"infring", r"indemn", r"infraccion"]),
    "open_source": ("Third-party and open-source components", "Componentes de terceros y de código abierto", "Disclose third-party and open-source components and their licences.",
                    ["Is there a list of components and licences? Copyleft obligations?"], [r"open source", r"third[- ]party (software|components)", r"codigo abierto", r"software libre"]),
    "audit": ("Licence audit", "Auditoría de licencias", "Let the licensor verify compliance.", ["Notice, frequency, cost allocation?"],
              [r"\baudit", r"auditor", r"verificacion del cumplimiento"]),
    "escrow": ("Source code escrow", "Depósito del código fuente", "Protect the licensee if the licensor fails.", ["Release events? Escrow agent?"],
               [r"escrow", r"deposito del codigo", r"codigo fuente"]),
    # dpa (GDPR Art. 28)
    "dpa_subject_duration": ("Subject-matter and duration of the processing", "Objeto y duración del tratamiento", "Art. 28(3) requires the contract to set out the subject-matter and duration of the processing.",
                             ["What processing is covered and until when?"], [r"subject[- ]matter", r"duration", r"\bobjeto\b", r"duracion"]),
    "dpa_nature_purpose": ("Nature and purpose of the processing", "Naturaleza y finalidad del tratamiento", "Art. 28(3): the nature and purpose of the processing.",
                           ["Which operations (collection, storage, hosting) and for what purpose?"], [r"nature", r"purpose", r"naturaleza", r"finalidad"]),
    "dpa_data_subjects": ("Type of personal data and categories of data subjects", "Tipo de datos personales y categorías de interesados", "Art. 28(3): the type of personal data and categories of data subjects.",
                          ["Are special categories (Art. 9) included?"], [r"categories of data subjects", r"types? of personal data", r"categorias de interesados", r"tipo de datos", r"categorias de datos"]),
    "dpa_controller_rights": ("Obligations and rights of the controller", "Obligaciones y derechos del responsable", "Art. 28(3): the obligations and rights of the controller.",
                              ["Are the controller's instructions, audit rights and duties set out?"], [r"obligations and rights of the controller", r"obligaciones y derechos del responsable", r"obligaciones del responsable"]),
    "dpa_a_instructions": ("Processing only on documented instructions", "Tratamiento según instrucciones documentadas", "Art. 28(3)(a): process only on documented instructions from the controller, including on transfers to third countries, unless EU or Member State law requires otherwise (then inform the controller first).",
                           ["Where are the instructions documented?", "Does it cover third-country transfers?"],
                           [r"documented instructions", r"instructions (of|from) the controller", r"instrucciones documentadas", r"instrucciones del responsable"]),
    "dpa_b_confidentiality": ("Confidentiality of authorised persons", "Confidencialidad del personal autorizado", "Art. 28(3)(b): persons authorised to process the data are bound by confidentiality.",
                              ["Are staff and contractors under a written or statutory duty of confidentiality?"], [r"confidential", r"confidencialidad", r"deber de secreto"]),
    "dpa_c_security": ("Security of processing (Art. 32)", "Seguridad del tratamiento (art. 32)", "Art. 28(3)(c): take all measures required by Article 32.",
                       ["Are the technical and organisational measures described, e.g. in an annex?"],
                       [r"article 32", r"art\.? 32", r"security measures", r"technical and organi[sz]ational measures", r"articulo 32", r"medidas de seguridad", r"medidas tecnicas y organizativas"]),
    "dpa_d_subprocessors": ("Sub-processors", "Subencargados del tratamiento", "Art. 28(3)(d), 28(2) and 28(4): engage another processor only with the controller's prior specific or general written authorisation, flow down the same obligations, and remain liable.",
                            ["Specific or general authorisation? How are changes notified and objected to?", "Is there a list of current sub-processors?"],
                            [r"sub-?processor", r"sub-?processing", r"another processor", r"subencargad", r"subcontrat"]),
    "dpa_e_data_subject_rights": ("Assistance with data subject rights", "Asistencia en el ejercicio de derechos", "Art. 28(3)(e): assist the controller, by appropriate technical and organisational measures, in responding to data subject requests (Chapter III).",
                                  ["How fast must requests received by the processor be passed on?"],
                                  [r"data subjects?'? rights", r"rights of (the )?data subjects", r"data subject requests", r"derechos de los interesados", r"ejercicio de (los )?derechos"]),
    "dpa_f_assistance_32_36": ("Assistance with Articles 32 to 36", "Asistencia en las obligaciones de los arts. 32 a 36", "Art. 28(3)(f): assist with security, personal data breach notification and communication, DPIAs and prior consultation.",
                               ["Breach notification deadline to the controller?", "Help with DPIAs?"],
                               [r"articles 32 to 36", r"arts?\.? 32 (to|-) 36", r"articulos 32 a 36", r"breach", r"impact assessment", r"prior consultation", r"violacion de (la )?seguridad", r"brecha", r"evaluacion de impacto", r"consulta previa"]),
    "dpa_g_deletion_return": ("Deletion or return at the end", "Supresión o devolución al finalizar", "Art. 28(3)(g): at the controller's choice, delete or return all personal data at the end of the services and delete copies, unless law requires storage.",
                              ["Who chooses, and within what time? Certificate of deletion?"],
                              [r"delet", r"return (of )?(all )?(the )?personal data", r"destino de los datos", r"devolucion", r"supresion", r"destruccion"]),
    "dpa_h_audits": ("Information and audits", "Información y auditorías", "Art. 28(3)(h): make available all information needed to demonstrate compliance and allow and contribute to audits, including inspections.",
                     ["Audit frequency, notice and costs? Certifications accepted instead?"], [r"\baudit", r"inspection", r"auditori", r"inspeccion"]),
    "dpa_infringing_instructions": ("Notice of infringing instructions", "Aviso de instrucciones contrarias a la normativa", "Art. 28(3), last subparagraph: the processor immediately informs the controller if an instruction infringes data protection law.",
                                    ["Is the duty to warn stated?"], [r"infring", r"contrari[ao] (a|al) (la normativa|el rgpd|reglamento)", r"infrinja", r"vulnera"]),
    "dpa_transfers": ("International transfers", "Transferencias internacionales", "Chapter V GDPR: transfers outside the EEA need an adequacy decision or appropriate safeguards (e.g. standard contractual clauses).",
                      ["Where is the data hosted and accessed from? Which transfer tool?"],
                      [r"transfer", r"third countr", r"standard contractual clauses", r"transferencia", r"terceros paises", r"clausulas contractuales tipo"]),
    "dpa_breach": ("Personal data breach notification", "Notificación de violaciones de seguridad", "Art. 33(2): the processor notifies the controller without undue delay after becoming aware of a breach; the contract usually fixes a deadline and content.",
                   ["How many hours, to whom, with what information?"], [r"breach", r"violacion de (la )?seguridad", r"brecha"]),
}
DPA_REFS = {"dpa_subject_duration": "GDPR Art. 28(3)", "dpa_nature_purpose": "GDPR Art. 28(3)", "dpa_data_subjects": "GDPR Art. 28(3)",
            "dpa_controller_rights": "GDPR Art. 28(3)", "dpa_a_instructions": "GDPR Art. 28(3)(a)", "dpa_b_confidentiality": "GDPR Art. 28(3)(b)",
            "dpa_c_security": "GDPR Art. 28(3)(c)", "dpa_d_subprocessors": "GDPR Art. 28(3)(d)", "dpa_e_data_subject_rights": "GDPR Art. 28(3)(e)",
            "dpa_f_assistance_32_36": "GDPR Art. 28(3)(f)", "dpa_g_deletion_return": "GDPR Art. 28(3)(g)", "dpa_h_audits": "GDPR Art. 28(3)(h)",
            "dpa_infringing_instructions": "GDPR Art. 28(3), second subparagraph", "dpa_transfers": "GDPR Chapter V (Arts. 44-49)", "dpa_breach": "GDPR Art. 33(2)"}
MANDATORY_DPA = {k for k, v in DPA_REFS.items() if v.startswith("GDPR Art. 28")}
TYPES = {
    "nda": ("Non-disclosure agreement", ["parties", "nda_definition", "nda_exclusions", "nda_purpose_use", "confidentiality", "nda_compelled", "nda_duration",
                                         "nda_return", "nda_no_licence", "nda_remedies", "personal_data", "assignment", "entire_agreement", "governing_law", "signatures"]),
    "services": ("Services agreement", ["parties", "definitions", "scope", "service_levels", "fees_payment", "term_termination", "ip", "confidentiality",
                                        "personal_data", "warranties", "liability", "indemnity", "insurance", "assignment", "non_solicitation", "force_majeure",
                                        "notices", "entire_agreement", "governing_law", "signatures"]),
    "lease": ("Lease", ["parties", "premises", "permitted_use", "term_termination", "rent", "deposit", "expenses", "repairs", "alterations", "sublet",
                        "insurance", "handover", "personal_data", "notices", "governing_law", "signatures"]),
    "employment": ("Employment contract", ["parties", "position", "contract_duration", "probation", "working_time", "salary", "leave", "collective_agreement",
                                           "confidentiality", "ip", "non_compete", "employee_privacy", "notice_period", "governing_law", "signatures"]),
    "sale_of_goods": ("Sale of goods", ["parties", "goods", "fees_payment", "delivery", "risk_title", "inspection", "product_warranty", "liability",
                                        "force_majeure", "term_termination", "cisg", "governing_law", "entire_agreement", "signatures"]),
    "software_license": ("Software licence", ["parties", "definitions", "licence_grant", "restrictions", "fees_payment", "support", "ip", "warranties",
                                              "liability", "ip_indemnity", "open_source", "audit", "escrow", "confidentiality", "personal_data",
                                              "term_termination", "governing_law", "signatures"]),
    "dpa": ("Data processing agreement (GDPR Art. 28)", ["parties", "dpa_subject_duration", "dpa_nature_purpose", "dpa_data_subjects", "dpa_controller_rights",
                                                         "dpa_a_instructions", "dpa_b_confidentiality", "dpa_c_security", "dpa_d_subprocessors",
                                                         "dpa_e_data_subject_rights", "dpa_f_assistance_32_36", "dpa_g_deletion_return", "dpa_h_audits",
                                                         "dpa_infringing_instructions", "dpa_transfers", "dpa_breach", "liability", "governing_law", "signatures"]),
}


def fail(message):
    print(json.dumps({"error": message}))
    sys.exit(2)


def fold(text):
    """Lower-case and strip accents character by character, keeping positions aligned with the original."""
    out = []
    for ch in text:
        base = unicodedata.normalize("NFD", ch)[0]
        low = base.lower()
        out.append(low if len(low) == 1 else base)
    return "".join(out)


def word_end(text, i):
    while i < len(text) and text[i].isalpha():
        i += 1
    return i


def snippet(text, start, end, width=200):
    left = max(text.rfind("\n", 0, start), text.rfind(". ", 0, start))
    left = left + 1 if left >= 0 else 0
    if start - left > 80:
        left = start - 80
    ends = [i for i in (text.find("\n", end), text.find(". ", end)) if i >= 0]
    right = min(ends) + 1 if ends else len(text)
    if right - left > width:
        right = left + width
    return " ".join(text[left:right].split())


def search(text, folded, patterns):
    best = None
    for pattern in patterns:
        m = re.search(r"(?<![a-z0-9])" + pattern, folded)
        if m and (best is None or m.start() < best[0].start()):
            best = (m, pattern)
    if best is None:
        return None
    m, _ = best
    return {"matched": text[m.start():word_end(folded, m.end())], "snippet": snippet(text, m.start(), m.end())}


def main():
    try:
        request = json.load(sys.stdin)
    except ValueError as error:
        fail(f"stdin is not JSON: {error}")
    if not isinstance(request, dict):
        fail("input must be a JSON object")
    if request.get("list_types"):
        print(json.dumps({"contract_types": {k: {"title": v[0], "clauses": len(v[1])} for k, v in TYPES.items()}}))
        return
    ctype = request.get("contract_type")
    if ctype not in TYPES:
        fail("contract_type must be one of " + ", ".join(TYPES))
    lang = request.get("language", "en")
    if lang not in ("en", "es"):
        fail("language must be en or es")
    text = request.get("text")
    if text is not None and (not isinstance(text, str) or not text.strip()):
        fail("text must be the contract text as a non-empty string")
    if text is not None and len(text) > 2000000:
        fail("text is longer than 2,000,000 characters")
    folded = fold(text) if text else None
    title, ids = TYPES[ctype]
    clauses, found, missing = [], [], []
    for cid in ids:
        en, es, purpose, questions, patterns = C[cid]
        row = {"id": cid, "title": es if lang == "es" else en, "purpose": purpose, "questions": questions}
        if cid in DPA_REFS:
            row["legal_reference"] = DPA_REFS[cid]
            row["mandatory"] = cid in MANDATORY_DPA
        if text:
            hit = search(text, folded, patterns)
            row["status"] = "found" if hit else "missing"
            if hit:
                row.update(hit)
                found.append(cid)
            else:
                missing.append(cid)
        clauses.append(row)
    out = {"contract_type": ctype, "title": title, "language": lang, "clauses": clauses}
    if ctype == "dpa":
        out["mandatory_contents"] = ["28(3)(a)", "28(3)(b)", "28(3)(c)", "28(3)(d)", "28(3)(e)", "28(3)(f)", "28(3)(g)", "28(3)(h)"]
        out["mandatory_note"] = ("GDPR Art. 28(3) requires a contract or legal act setting out the subject-matter, duration, nature and purpose of the "
                                 "processing, the type of personal data, the categories of data subjects and the controller's obligations and rights, "
                                 "and stipulating points (a) to (h); standard contractual clauses under Art. 28(7)-(8) may be used.")
    if text:
        out["summary"] = {"found": len(found), "missing": len(missing), "total": len(ids), "missing_ids": missing}
        if ctype == "dpa":
            out["summary"]["mandatory_missing"] = [c for c in missing if c in MANDATORY_DPA]
        out["heuristic_note"] = HEURISTIC
    out["disclaimer"] = DISCLAIMER[lang]
    print(json.dumps(out, ensure_ascii=False))


if __name__ == "__main__":
    main()

Where it is served

The registry serves only AXLR clients, so a browser gets 403; fetch with a User-Agent that starts with axlr:

curl -A axlr-docs/1.0 -O https://registry.underpassai.com/tools/clause_checklist/1.0.0/manifest.json
curl -A axlr-docs/1.0 -O https://registry.underpassai.com/tools/clause_checklist/1.0.0/clause_checklist.py